Looking to hire Laravel developers? Try LaraJobs

laravel-altcha maintained by climactic

Description
ALTCHA proof-of-work spam protection for Laravel — drop-in middleware, validation rule, and challenge endpoint.
Last update
2026/04/17 23:53 (dev-main)
License
Downloads
0

Comments
comments powered by Disqus

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.

Discord Latest Version on Packagist GitHub Tests Action Status GitHub Code Style Action Status Total Downloads Sponsor on GitHub Support on Ko-fi

📖 Table of Contents

Features

  • 🛡️ Drop-in middleware — protect any POST route with a single altcha alias
  • Validation rule — use new Altcha() in FormRequests and inline validators
  • 🎯 Auto-registered challenge endpointGET /altcha works 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 altcha web 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 component
  • resources/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

Star History Chart

📦 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.