Spam-Schutz: Rate-Limiting, Link-Heuristik & Spam-Logging
Mehrschichtiger Formular-Schutz ohne externe Dienste, ergänzend zu Honeypot und HMAC-Time-Trap: - rate_limit_ok(): pro IP+Route (5/10 min), Tages-Cap (100/Tag), Token-Replay - client_ip() / log_spam() (abgewiesene Versuche -> storage/logs/spam.log) - Honeypot-Feld website -> company_url umbenannt (contact-/membership-form) - Ratelimit-Hinweis in form.js - storage/ratelimit/ (gitignored bis auf .gitkeep) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HGzc6GhWhmLJt1jC2q1SRZ
This commit is contained in:
@@ -7,7 +7,7 @@ declare(strict_types=1);
|
||||
* $intro array{title, text}
|
||||
* Felder rendern ausschließlich über component('form-field') — siehe CLAUDE.md.
|
||||
* Liest Flash-Status aus Query-Params (?sent=1 / ?error=...) — PRG-Muster.
|
||||
* Spam-Schutz: Honeypot "website" + signierter Timestamp "ft" (form_token()).
|
||||
* Spam-Schutz: Honeypot "company_url" + signierter Timestamp "ft" (form_token()).
|
||||
*/
|
||||
$club = json_load('club');
|
||||
|
||||
@@ -17,6 +17,7 @@ $sent = isset($_GET['sent']);
|
||||
$error = isset($_GET['error']) ? (string) $_GET['error'] : null;
|
||||
$errorMessages = [
|
||||
'validation' => 'Bitte prüfe deine Eingaben – Pflichtfelder fehlen oder die E-Mail-Adresse ist ungültig.',
|
||||
'ratelimit' => 'Zu viele Anfragen in kurzer Zeit. Bitte versuche es in ein paar Minuten erneut.',
|
||||
'mail' => 'Deine Nachricht konnte gerade nicht versendet werden. Bitte versuche es später erneut oder schreib uns direkt an ' . $club['email'] . '.',
|
||||
];
|
||||
|
||||
@@ -68,8 +69,8 @@ $f = [
|
||||
|
||||
<?php /* Honeypot — für Menschen unsichtbar, Bots füllen es aus */ ?>
|
||||
<div class="visually-hidden" aria-hidden="true">
|
||||
<label for="contact-website">Website (bitte leer lassen)</label>
|
||||
<input type="text" id="contact-website" name="website" tabindex="-1" autocomplete="off">
|
||||
<label for="contact-company-url">Bitte dieses Feld leer lassen</label>
|
||||
<input type="text" id="contact-company-url" name="company_url" tabindex="-1" autocomplete="off">
|
||||
</div>
|
||||
<input type="hidden" name="ft" value="<?= e(form_token()) ?>">
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ declare(strict_types=1);
|
||||
* $fee_options array Beitragsklassen als Strings (Whitelist – muss zur Action passen)
|
||||
* Felder rendern ausschließlich über component('form-field') — siehe CLAUDE.md.
|
||||
* Liest Flash-Status aus Query-Params (?sent=1 / ?error=…) — PRG-Muster.
|
||||
* Spam-Schutz: Honeypot "website" + signierter Timestamp "ft" (form_token()).
|
||||
* Spam-Schutz: Honeypot "company_url" + signierter Timestamp "ft" (form_token()).
|
||||
*/
|
||||
$club = json_load('club');
|
||||
$fee_options = $fee_options ?? [];
|
||||
@@ -17,6 +17,7 @@ $sent = isset($_GET['sent']);
|
||||
$error = isset($_GET['error']) ? (string) $_GET['error'] : null;
|
||||
$errorMessages = [
|
||||
'validation' => 'Bitte prüfe deine Eingaben – Pflichtfelder fehlen oder die E-Mail-Adresse ist ungültig.',
|
||||
'ratelimit' => 'Zu viele Anfragen in kurzer Zeit. Bitte versuche es in ein paar Minuten erneut.',
|
||||
'mail' => 'Dein Antrag konnte gerade nicht versendet werden. Bitte versuche es später erneut oder schreib uns direkt an ' . $club['email'] . '.',
|
||||
];
|
||||
|
||||
@@ -102,8 +103,8 @@ $f = [
|
||||
|
||||
<?php /* Honeypot — für Menschen unsichtbar, Bots füllen es aus */ ?>
|
||||
<div class="visually-hidden" aria-hidden="true">
|
||||
<label for="mm-website">Website (bitte leer lassen)</label>
|
||||
<input type="text" id="mm-website" name="website" tabindex="-1" autocomplete="off">
|
||||
<label for="mm-company-url">Bitte dieses Feld leer lassen</label>
|
||||
<input type="text" id="mm-company-url" name="company_url" tabindex="-1" autocomplete="off">
|
||||
</div>
|
||||
<input type="hidden" name="ft" value="<?= e(form_token()) ?>">
|
||||
|
||||
|
||||
Reference in New Issue
Block a user