Markus Michalski FAQ

ALTCHA: Technical Details

ALTCHA spam protection for Sylius - check chain as a diagram, the three enforcement paths (form, route, event), plugin structure and the six entities with their own tables

Deutsche Version

Back to overview

ALTCHA: Technical Details

Live Demo: Try this and other plugins live β€” no installation, no risk: demo.markus-michalski.net

How the checks run

graph TD
    A["Submission / Request"] --> B{"Channel enabled?"}
    B -->|"No"| C["Unprotected"]
    B -->|"Yes"| D{"Target ticked?"}
    D -->|"No"| C
    D -->|"Yes"| E["Rate limit"]
    E -->|"exceeded"| F["429 / rate_limited"]
    E -->|"ok"| G["Honeypot + fill time"]
    G -->|"failed"| H["Reject - form error / 422"]
    G -->|"ok"| I{"Disposable-mail / gibberish active?"}
    I -->|"flagged"| H
    I -->|"off or passed"| J["Verify ALTCHA proof"]
    J -->|"invalid or reused"| H
    J -->|"valid"| K["Submission accepted"]

Rate limit, honeypot, minimum fill time and the content checks run ahead of the proof-of-work verification (cheapest first) through all three enforcement mechanisms alike - a target is checked the same way regardless of which path reaches it.

The three enforcement paths

graph LR
    A["Twig form (Storefront)"] --> D["Form validation"]
    B["Route (login / checkout / Shop API)"] --> E["Route check on kernel.request"]
    C["Plugin event (support form, newsletter)"] --> F["Event listener"]
    D --> G["Submission guard chain"]
    E --> G
    F --> G
    G --> H["Channel settings"]
Mechanism Used for Runs
Form validation registration, contact, forgotten password, custom forms After the regular constraints, so a form that is invalid for another reason does not consume a challenge. Live Component re-renders are skipped.
Route check login, checkout sign-in, Shop API operations On kernel.request, before the firewall. For storefront routes the locale is already set by then; for API routes Sylius sets the request locale from the channel later, so the check reads the locale from Sylius' locale context there instead.
Event listener support form, newsletter, and any target that declares events On the other plugin's own rejectable event (storefront and API alike)

A form rejection is added as a form error and also flashed, because Sylius' shop templates render fields individually and never a form's own errors.

Plugin structure

src/
β”œβ”€β”€ Challenge/          # Challenge generation and signing
β”œβ”€β”€ Command/            # SetupCommand, PurgeCommand, UpdateDisposableDomainsCommand
β”œβ”€β”€ Controller/         # ChallengeController, BlockedDomainsController
β”œβ”€β”€ DependencyInjection/
β”œβ”€β”€ DisposableEmail/    # Disposable-mail guard, domain list, override entities
β”œβ”€β”€ Enforcement/        # Form type extension, route listener, event listener/registrar
β”œβ”€β”€ Entity/             # ChannelSettings, CustomTarget, DisposableDomainOverride, GibberishLogEntry, RateLimitHit, UsedChallenge
β”œβ”€β”€ Form/Type/          # Admin form types
β”œβ”€β”€ Gibberish/          # Scorer, trigram tables, build script
β”œβ”€β”€ Guard/              # SubmissionGuard orchestrating all checks, Rejection enum
β”œβ”€β”€ Menu/               # Admin sidebar entry
β”œβ”€β”€ Migrations/         # Seven Doctrine migrations
β”œβ”€β”€ RateLimit/          # Rate limiter
β”œβ”€β”€ Request/
β”œβ”€β”€ Security/           # Secret cipher (libsodium)
β”œβ”€β”€ Settings/           # Channel settings provider/factory
β”œβ”€β”€ Submission/
β”œβ”€β”€ Target/             # ProtectionTarget, registry, built-in targets
β”œβ”€β”€ Twig/               # mmd_altcha_widget() function
β”œβ”€β”€ Validator/
└── Verification/       # Proof verification and replay guard
config/
β”œβ”€β”€ grids/              # Four admin grids
β”œβ”€β”€ routes/             # admin.yaml, shop.yaml
└── twig_hooks/         # admin.yaml (title-block and action hooks in the admin), shop.yaml (storefront widget hooks, incl. mmd_altcha.widget*)

Entities

ChannelSettings, CustomTarget, DisposableDomainOverride, GibberishLogEntry, RateLimitHit and UsedChallenge: six tables named mmd_sylius_altcha_*. There are seven migrations:

Operating notes