Skip to content

About

A Flarum PoW CAPTCHA plugin. Security Self-hosted

Resources

Security policy

Stars

2 stars

Watchers

0 watching

Forks

Latest commit

ย 

History

183 Commits

Folders and files

NameName
Last commit message
Last commit date
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 

Repository files navigation

PoW CAPTCHA for Flarum

Packagist License

A local Proof-of-Work CAPTCHA extension for Flarum that protects login, registration and password-reset forms without relying on any external service (no Cloudflare, no Google reCAPTCHA, no cookies).

How it works

  1. When an auth modal opens, the browser silently asks the Flarum API for a challenge for that specific form (login, sign-up, password reset).
  2. The server returns a signed, stateless challenge. Its difficulty, issue time and form are protected by an HMAC, so the browser cannot alter them and the server stores nothing at this point.
  3. The browser solves a SHA-256 puzzle (finds a nonce N such that SHA-256(challenge:N) starts with D hex zeros).
  4. The token challenge:nonce is sent with the form. The server checks the signature, the form, the age, the work done and that the token has never been used (atomically), and rejects the request otherwise.

Bots must repeat the work for every single request, and the work grows automatically for networks that misbehave (see Adaptive difficulty).

Features

  • ๐Ÿ”’ No external services โ€“ fully self-hosted, no cookies
  • โšก Invisible to users โ€“ solved in the background while they fill in the form
  • ๐Ÿงฎ Three difficulty levels โ€“ plus adaptive difficulty that raises the work (up to +2 steps) for networks with repeated failures or many sign-ups, and +1 for everyone during a site-wide attack
  • โœ๏ธ Signed stateless challenges โ€“ difficulty and form cannot be forged; a login token cannot be used on the sign-up form
  • ๐Ÿ” Atomic single use โ€“ a token can be consumed only once, even with parallel requests (uses a cache lock when available)
  • ๐Ÿชค Honeypot and time trap โ€“ invisible traps for form-filling bots (sign-up must take at least 2 s)
  • ๐Ÿ›ก๏ธ Optional protection of /api/token โ€“ the public API login endpoint that bypasses the login form
  • ๐Ÿ“‹ Anonymous event log in the admin panel โ€“ last 30 passed / 30 failed checks, no IP address or personal data, self-pruning, with a Clear log button
  • โ™ฟ Accessible โ€“ polite live-region announcements, no visual puzzle, no dragging, honeypot hidden from assistive technology
  • ๐ŸŒ Italian & English, ๐ŸŒ“ dark / light mode, โœ… Flarum 2.x (the PHP side also keeps a Flarum 1.x code path, but the shipped JavaScript is built for 2.x)

Requirements

Dependency Version
PHP โ‰ฅ 8.1
Flarum ^1.8 or ^2.0

Screenshot

Screenshot 2026-05-14 205357 1778785001-133386-screenshot-2026-05-14-205627

Disclaimer

This software is provided "AS IS", without any warranty. While it has been tested and reasonable efforts are made to ensure security and reliability, no guarantees are provided. As an open project, anyone may contribute or report issues, but this does not imply endorsement or liability from the maintainers.

You use this software entirely at your own risk. The authors and contributors are not liable for any damages, data loss, or unexpected behavior resulting from its use, modification, or distribution. Always review and test the code independently before deploying it in critical or production environments.

Installation

composer require peopleinside/fla-powcaptcha
For Flarum 1.8 use the version 2.3

The future will be Flarum 2.0 that support the latest release.

Install with

composer require peopleinside/fla-powcaptcha:2.3

Update

composer update peopleinside/fla-powcaptcha
php flarum migrate
php flarum cache:clear

migrate is required: it creates the log table and converts the old difficulty setting (see Upgrading from 3.7.x).

How to remove

composer remove peopleinside/fla-powcaptcha

Then enable the extension in the Flarum admin panel.

Configuration

Go to Admin โ†’ Extensions โ†’ PoW CAPTCHA.

Setting Default Description
Enable on Login / Registration / Password Reset on Which forms are protected
Difficulty level 2 โ€“ Balanced Level 1 = 3 hex zeros (~4k hashes), level 2 = 4 (~65k), level 3 = 5 (~1M). Approximate time on a recent PC: instant / โ‰ˆ 0.1 s / โ‰ˆ 1โ€“3 s (phones are 3โ€“6ร— slower)
Adaptive difficulty on Raises the difficulty for suspicious networks (hard ceiling: 6 zeros)
Protect the API login endpoint on for new installs, off for existing ones Requires a CAPTCHA token on direct POST /api/token calls
Keep an anonymous event log on Last 30 passed + 30 failed checks, no personal data

Upgrading from 3.7.x

The old difficulty setting (3/4/5) is converted once, by a migration, to the new level (1/2/3) with exactly the same real difficulty: old 1 or 3 โ†’ level 1, old 2 or 4 โ†’ level 2, old 5 โ†’ level 3. The old value is left in the database, so downgrading still works. Run php flarum migrate and php flarum cache:clear after updating.

Protecting /api/token

Flarum's login form posts to /login, which calls /api/token internally, so a bot that calls /api/token directly never meets the login CAPTCHA. With this option on, direct calls must include captchaToken (and no value in pow_website) obtained from GET /api/powcaptcha/challenge?action=token. Turn it off if mobile apps or integrations log in through the API. It only takes effect on Flarum core versions that mark internal API calls (RequestUtil::isInternal); on older cores it is simply inactive, never blocking the normal form. Flarum's own login throttler (5 failed attempts per minute per account and IP) keeps applying in all cases.

Development (for contributors only)

The extension ships with pre-compiled frontend assets (js/dist/*), so no build step is needed to install or use it. Always rebuild js/dist before tagging a release so the shipped bundle matches js/src:

cd js
npm ci
npm run build

The PHP logic is covered by plain-PHP tests (no Flarum installation required): php tests/run.php. After every build run bash tests/js/run.sh (Node only): it loads the shipped bundles the way Flarum does and catches boot-time crashes before they reach a forum.

Security Notes

  • Challenges are valid for 5 minutes, signed with HMAC-SHA256 using a per-installation secret (created on install, stored in the settings table), and single use.
  • The server recomputes the hash itself; difficulty and form come from the signed challenge, never from the client.
  • Fail-closed: if the cache is unavailable, verification fails instead of letting requests through.
  • Challenges issued to networks that already look abusive are bound to that network, so they cannot be requested from a clean IP and used from a dirty one. Normal visitors are never bound (no problems with dual-stack or mobile networks).
  • Challenge requests are rate limited (30 per minute per IPv4 address or IPv6 /64).
  • Limits you should know about: Proof-of-Work raises the cost of every request but does not tell humans from bots, and it does not stop a determined attacker with a headless browser or a native SHA-256 solver. Combine it with e-mail confirmation, approval of first posts and reputation checks. On busy sites prefer Redis / APCu / Memcached over the file cache: the file driver does not delete expired entries on its own.

Anonymous log and privacy

The log stores only time, form, outcome, a technical reason and difficulty. It never stores IP addresses (not even truncated or hashed), user agents, usernames or e-mail addresses. Abuse counters used for rate limiting and adaptive difficulty live in the cache, are keyed by an HMAC of the network (never the raw IP) and expire within minutes. Even so, mention the CAPTCHA in your privacy policy; this is not legal advice.

Running Behind a Reverse Proxy / Load Balancer

Important: this extension (like Flarum core) uses the connection's REMOTE_ADDR as the client address and deliberately ignores X-Forwarded-For, Client-IP and similar headers, which anyone can forge. Flarum has no proxy_headers / proxy_all config keys (older versions of this README suggested them mistakenly โ€” they have no effect on core and must not be relied on).

If you use a reverse proxy or a CDN, configure the web server so REMOTE_ADDR carries the real visitor address, for example:

  • nginx: set_real_ip_from <proxy CIDR>; real_ip_header X-Forwarded-For; (or CF-Connecting-IP for Cloudflare) with real_ip_recursive on;
  • Apache: mod_remoteip with RemoteIPHeader and RemoteIPTrustedProxy

If REMOTE_ADDR is a private or reserved address (typical when that is not configured), the per-IP features โ€” rate limit, adaptive difficulty and network binding โ€” are switched off automatically instead of treating all visitors as one; the CAPTCHA itself keeps working.

Troubleshooting

The forum still seems to run old code after an update (typical when the version number stays the same, because Composer may serve its cached copy):

  1. Replace the extension folder completely (delete it, then copy the whole content of the release); do not overlay files onto the old ones.
  2. Run composer clear-cache, then composer dump-autoload and php flarum cache:clear from the Flarum root.
  3. Check the server side: ls vendor/peopleinside/fla-powcaptcha/src must list Provider, Middleware, Service, Controllerโ€ฆ, and extend.php must contain autoload.php (grep -c autoload.php vendor/peopleinside/fla-powcaptcha/extend.php โ†’ 1).
  4. Check the browser side: open the forum in a private window, press F12 and type flarum.extensions['peopleinside-fla-powcaptcha']. It must answer {build: "3.8.0+โ€ฆ"}; if it does not, the old JavaScript is still being served.

Class "PeopleInside\PowCaptcha\โ€ฆ" not found in storage/logs: the files are incomplete or Composer's classmap is stale. Follow the steps above.

A form is broken or empty: in Admin โ†’ Extensions โ†’ PoW CAPTCHA switch off the CAPTCHA for that form. If the problem remains, it is not caused by this extension.

License

Apache-2.0

About

A Flarum PoW CAPTCHA plugin. Security Self-hosted

Resources

Security policy

Stars

2 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages