climactic / laravel-altcha
ALTCHA proof-of-work spam protection for Laravel โ drop-in middleware, validation rule, and challenge endpoint.
Fund package maintenance!
Requires
- php: ^8.4
- altcha-org/altcha: ^2.0
- illuminate/contracts: ^11.0||^12.0||^13.0
- spatie/laravel-package-tools: ^1.16
Requires (Dev)
- larastan/larastan: ^3.0
- laravel/pint: ^1.14
- nunomaduro/collision: ^8.8
- orchestra/testbench: ^10.0||^9.0
- pestphp/pest: ^4.0
- pestphp/pest-plugin-arch: ^4.0
- pestphp/pest-plugin-laravel: ^4.0
- phpstan/extension-installer: ^1.4
- phpstan/phpstan-deprecation-rules: ^2.0
- phpstan/phpstan-phpunit: ^2.0
README
Laravel Altcha
Drop-in ALTCHA proof-of-work spam protection for Laravel. Privacy-first, self-hosted, no third-party services, no tracking โ just a middleware, a validation rule, and a challenge endpoint.
๐ Table of Contents
- Laravel Altcha
Features
- ๐ก๏ธ Drop-in middleware โ protect any POST route with a single
altchaalias - โ
Validation rule โ use
new Altcha()in FormRequests and inline validators - ๐ฏ Auto-registered challenge endpoint โ
GET /altchaworks out of the box, fully customizable - ๐ Replay prevention โ challenges are single-use, cached by signature
- ๐ Pluggable algorithms โ PBKDF2, Argon2id, or Scrypt
- โก Optional fast verification โ skip re-derivation with a secondary HMAC key
- ๐งฉ Framework-agnostic frontend โ React/TypeScript stubs shipped; any framework supported via the
altchaweb component - ๐ i18n-friendly โ all user-facing messages routed through
__() - ๐งช Tested โ 14 feature tests across middleware, rule, and endpoint
- ๐ Privacy-first โ no cookies, no fingerprinting, no third-party calls, GDPR/HIPAA/CCPA compliant
Built on altcha-org/altcha v2 (PoW v2).
๐ Sponsors
Your logo here โ Become a sponsor and get your logo featured in this README and on our website.
Interested in title sponsorship? Contact us at sponsors@climactic.co for premium placement and recognition.
๐ฆ Installation
Install the package via Composer:
composer require climactic/laravel-altcha
Publish the config:
php artisan vendor:publish --tag="laravel-altcha-config"
Add the required env vars:
ALTCHA_ENABLED=true ALTCHA_HMAC_SECRET=<long-random-string>
๐ก Generate a strong secret with
php -r "echo bin2hex(random_bytes(32));".
โ๏ธ Configuration
The published config/altcha.php exposes everything you might want to tune:
return [ 'enabled' => (bool) env('ALTCHA_ENABLED', false), 'hmac_secret' => env('ALTCHA_HMAC_SECRET'), 'hmac_key_secret' => env('ALTCHA_HMAC_KEY_SECRET'), 'algorithm' => env('ALTCHA_ALGORITHM', 'pbkdf2'), 'cost' => (int) env('ALTCHA_COST', 10000), 'expires' => (int) env('ALTCHA_EXPIRES', 300), 'memory_cost' => ..., 'parallelism' => ..., 'cache_store' => env('ALTCHA_CACHE_STORE'), 'field' => env('ALTCHA_FIELD', 'altcha'), 'route' => [ 'enabled' => true, 'path' => 'altcha', 'name' => 'altcha.challenge', 'prefix' => '', 'domain' => null, 'middleware' => ['web'], ], ];
Environment Variables
| Key | Env | Default | Notes |
|---|---|---|---|
enabled |
ALTCHA_ENABLED |
false |
Master switch. When false, middleware and rule pass-through and the endpoint returns 404. |
hmac_secret |
ALTCHA_HMAC_SECRET |
โ | Required when enabled. Used to sign challenges. |
hmac_key_secret |
ALTCHA_HMAC_KEY_SECRET |
null |
Optional. Enables the fast verification path (skips re-derivation). |
algorithm |
ALTCHA_ALGORITHM |
pbkdf2 |
pbkdf2, argon2id, or scrypt. |
cost |
ALTCHA_COST |
10000 |
PoW iterations / time cost. Higher = harder for bots. |
expires |
ALTCHA_EXPIRES |
300 |
Challenge lifetime in seconds. Also used as the replay cache TTL. |
memory_cost |
ALTCHA_MEMORY_COST |
null |
Argon2id / Scrypt only. |
parallelism |
ALTCHA_PARALLELISM |
null |
Scrypt only. |
cache_store |
ALTCHA_CACHE_STORE |
default | Cache store used for replay prevention. |
field |
ALTCHA_FIELD |
altcha |
Request field name the middleware/rule read from. |
route.enabled |
ALTCHA_ROUTE_ENABLED |
true |
Disable to register the endpoint yourself. |
route.path |
ALTCHA_ROUTE_PATH |
altcha |
Challenge endpoint path. |
route.name |
ALTCHA_ROUTE_NAME |
altcha.challenge |
Route name for route() helpers. |
route.prefix |
ALTCHA_ROUTE_PREFIX |
'' |
Route group prefix. |
route.domain |
ALTCHA_ROUTE_DOMAIN |
null |
Route group domain. |
Algorithms
| Algorithm | Extension | When to use |
|---|---|---|
pbkdf2 |
built-in | Default. Works everywhere. Good baseline. |
argon2id |
ext-sodium |
Strongest. Memory-hard โ resistant to ASIC/GPU acceleration. |
scrypt |
php-scrypt |
Memory-hard alternative. |
๐ Usage
๐ก๏ธ Middleware
Protect any POST route with the altcha middleware alias:
use Illuminate\Support\Facades\Route; Route::post('/contact', ContactController::class)->middleware('altcha'); Route::post('/register', RegisterController::class)->middleware('altcha');
Works inside a middleware group too:
Route::middleware(['web', 'altcha'])->group(function () { Route::post('/enterprise', EnterpriseController::class); Route::post('/waitlist', WaitlistController::class); });
Failed verifications throw a ValidationException with an error on the altcha field โ so Inertia/Blade forms display the error like any other validation failure.
โ Validation Rule
For FormRequests and inline validators:
use Climactic\Altcha\Rules\Altcha; public function rules(): array { return [ 'email' => ['required', 'email'], 'altcha' => [new Altcha()], ]; }
The rule is implicit โ it runs even when the field is empty, so you don't need required unless you want its error message. When altcha.enabled is false, it passes silently.
๐ฏ Challenge Endpoint
GET /altcha is registered automatically and returns a signed challenge:
{
"parameters": {
"algorithm": "pbkdf2",
"salt": "...",
"cost": 10000
},
"signature": "..."
}
Customize via config/altcha.php โ path, prefix, domain, and middleware group are all tunable. Set route.enabled to false and register the controller yourself if you need full control:
use Climactic\Altcha\Http\Controllers\AltchaChallengeController; Route::get('/security/challenge', AltchaChallengeController::class) ->middleware(['web', 'throttle:60,1']);
๐งฉ Frontend Integration
Install the ALTCHA web component:
npm install altcha
React (Inertia) stubs
Publish the TypeScript wrappers:
php artisan vendor:publish --tag="altcha-frontend"
This copies:
resources/js/components/altcha-widget.tsxโ React wrapper around the web componentresources/js/types/altcha.d.tsโ JSX types for<altcha-widget>
Then use it in any form:
import { AltchaWidget } from '@/components/altcha-widget'; export default function ContactForm() { return ( <form method="post" action="/contact"> <input name="email" type="email" required /> <textarea name="message" required /> <AltchaWidget challengeUrl="/altcha" /> <button type="submit">Send</button> </form> ); }
The widget solves the challenge in a web worker and injects the solution into a hidden altcha input at submit time โ totally invisible to your users.
Other frameworks
Vue, Svelte, Solid, Lit, Alpine, and plain HTML work out of the box with the altcha npm package โ no wrapper needed:
<altcha-widget challengeurl="/altcha" auto="onsubmit" floating="bottom"></altcha-widget>
See the official framework starters for idiomatic integrations.
๐ Replay Prevention
Every valid solution is recorded in the cache using the challenge signature as the key. A second submission of the same payload fails with a replay error:
Security challenge has already been used. Please try again.
The record TTL equals altcha.expires (so entries self-expire when the challenge itself would have). Use the cache_store config to isolate this from your application's default cache.
๐งฎ Custom Verification
Need to verify outside the middleware/rule (e.g. an API controller, a queued job)? Resolve the Verifier:
use Climactic\Altcha\Support\Verifier; $result = app(Verifier::class)->verify($request->input('altcha')); if (! $result->verified) { return response()->json([ 'error' => 'captcha', 'reason' => $result->reason, // missing | malformed | invalid | expired | replay ], 422); }
๐ API Reference
๐ง Classes
| Class | Purpose |
|---|---|
Climactic\Altcha\Http\Middleware\VerifyAltcha |
Middleware registered under the altcha alias. |
Climactic\Altcha\Http\Controllers\AltchaChallengeController |
Invokable controller backing GET /altcha. |
Climactic\Altcha\Rules\Altcha |
Implicit ValidationRule implementation. |
Climactic\Altcha\Support\Verifier |
Core verification service. Resolvable from the container. |
Climactic\Altcha\Support\VerificationResult |
Immutable result value object (verified, reason, replayKey). |
Climactic\Altcha\Support\AlgorithmFactory |
Produces a DeriveKeyInterface for the configured algorithm. |
๐ง Middleware Behavior
VerifyAltcha intentionally bypasses verification in a few cases so you can apply it broadly without extra logic:
| Condition | Outcome |
|---|---|
altcha.enabled is false |
Pass-through |
| Request method is not POST | Pass-through |
| User is authenticated | Pass-through |
| Missing/invalid payload | ValidationException on the altcha field |
| Valid payload | Marked used, request continues |
๐งพ Verifier Reasons
VerificationResult::$reason is one of:
| Constant | String | Meaning |
|---|---|---|
Verifier::REASON_MISSING |
missing |
No payload was submitted. |
Verifier::REASON_MALFORMED |
malformed |
Payload wasn't valid base64 / JSON / shape. |
Verifier::REASON_INVALID |
invalid |
Signature or derived key didn't match. |
Verifier::REASON_EXPIRED |
expired |
Challenge passed expiresAt. |
Verifier::REASON_REPLAY |
replay |
Payload was already accepted once. |
๐งช Testing
composer test
Type check + lint:
composer analyse composer format
๐ Changelog
Please see CHANGELOG for more information on what has changed recently.
๐ค Contributing
Please see CONTRIBUTING for details. You can also join our Discord server to discuss ideas and get help: Discord Invite.
๐ Security Vulnerabilities
Please report security vulnerabilities to security@climactic.co.
๐ Support This Project
Laravel Altcha is free and open source, built and maintained with care. If this package helps keep spam off your forms or saves you a Cloudflare Turnstile bill, please consider supporting its continued development.
โญ Star History
๐ฆ Other Packages
Check out our other Laravel packages:
| Package | Description |
|---|---|
| ๐ก๏ธ laravel-spam | Anti-spam toolkit: disposable-email and blocklist rules, StopForumSpam lookups, middleware, and a check API. Pairs naturally with Laravel Altcha. |
| ๐ณ laravel-credits | Ledger-based credit system for virtual currencies, reward points, and credit-based features, with transfers, metadata querying, and historical balances. |
| ๐งพ laravel-polar | Polar.sh billing integration for Laravel โ subscriptions, one-off purchases, webhooks, and license keys. |
๐ License
The MIT License (MIT). Please see License File for more information.
โ๏ธ Disclaimer
This package is not affiliated with Laravel or ALTCHA. It's for Laravel but is not by Laravel. Laravel is a trademark of Taylor Otwell. ALTCHA is a trademark of Daniel Regeci / ALTCHA.