Files
kaffeekasse-saas/scripts/check-spam-guard.php
T
clemensandClaude Opus 5 86334c2752 Offene Registrierung gegen Bot-Massenanlage absichern
Auf der Testumgebung liefen seit Juli 2026 taeglich 10-20 automatisierte
Registrierungen, die zufaellige Namen mit fremden echten E-Mail-Adressen
kombinierten - der Server verschickte Vertrags- und Verifikationsmail an
Unbeteiligte. Das IP-Rate-Limit griff nicht, weil jede Anfrage ueber eine
eigene Rechenzentrums-IP kam.

app/spam-guard.php ergaenzt daher Honigtopf, Zeitfalle, eine globale
Notbremse ueber alle IPs hinweg und eine MX/A-Pruefung der Mail-Domain.
Die ersten drei antworten mit derselben generischen Meldung wie das
Rate-Limit, damit die Antwort nicht verraet, welche Huerde angeschlagen
hat; Treffer landen im Error-Log. Bewusst ohne Captcha, um keinen
Drittanbieter in den Registrierungspfad zu holen.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-23 23:09:38 +02:00

136 lines
3.6 KiB
PHP

<?php
declare(strict_types=1);
/**
* Regressionstest fuer app/spam-guard.php - die Huerden, die seit der
* Bot-Registrierungswelle auf der Testumgebung vor register.php stehen.
* Braucht keine Datenbank: geprueft werden Honigtopf, Zeitfalle,
* Domain-Aufloesung und die Gestalt des versteckten Feldes.
*/
require __DIR__ . '/../app/spam-guard.php';
function spam_guard_assert(string $label, bool $condition, array &$failures, int &$passes): void
{
if ($condition) {
$passes++;
echo "PASS {$label}\n";
return;
}
$failures[] = $label;
echo "FAIL {$label}\n";
}
$failures = [];
$passes = 0;
// --- Honigtopf ---
$_POST = [];
spam_guard_assert('Leeres Honigtopf-Feld ist unauffaellig', !app_spam_guard_honeypot_tripped(), $failures, $passes);
$_POST['contact_reference'] = 'https://example.com';
spam_guard_assert('Ausgefuellter Honigtopf schlaegt an', app_spam_guard_honeypot_tripped(), $failures, $passes);
$_POST['contact_reference'] = ' ';
spam_guard_assert('Nur Leerzeichen zaehlen nicht als Treffer', !app_spam_guard_honeypot_tripped(), $failures, $passes);
$_POST = [];
// --- Zeitfalle ---
$_SESSION = [];
spam_guard_assert(
'Ohne ausgeliefertes Formular gilt die Antwort als zu schnell',
app_spam_guard_submitted_too_fast('register'),
$failures,
$passes
);
app_spam_guard_mark_form_rendered('register');
spam_guard_assert(
'Sofortiges Absenden nach dem Rendern schlaegt an',
app_spam_guard_submitted_too_fast('register'),
$failures,
$passes
);
// Menschliche Bearbeitungszeit simulieren, ohne den Test warten zu lassen.
$_SESSION['spam_guard_rendered']['register'] = time() - (APP_SPAM_GUARD_MIN_SECONDS + 1);
spam_guard_assert(
'Nach ausreichender Bearbeitungszeit geht die Antwort durch',
!app_spam_guard_submitted_too_fast('register'),
$failures,
$passes
);
spam_guard_assert(
'Der Zeitstempel gilt nur fuer sein eigenes Formular',
app_spam_guard_submitted_too_fast('anderes-formular'),
$failures,
$passes
);
// --- Domain-Aufloesung ---
spam_guard_assert(
'Domain mit MX-Eintrag wird akzeptiert',
app_spam_guard_email_domain_resolves('jemand@gmail.com'),
$failures,
$passes
);
spam_guard_assert(
'Erfundene Domain wird abgewiesen',
!app_spam_guard_email_domain_resolves('jemand@diese-domain-gibt-es-ganz-sicher-nicht-4711.invalid'),
$failures,
$passes
);
spam_guard_assert(
'Adresse ohne Punkt in der Domain wird abgewiesen',
!app_spam_guard_email_domain_resolves('jemand@localhost'),
$failures,
$passes
);
spam_guard_assert(
'Zeichenkette ohne @ wird abgewiesen',
!app_spam_guard_email_domain_resolves('keine-adresse'),
$failures,
$passes
);
// --- Gestalt des versteckten Feldes ---
$field = app_spam_guard_honeypot_field();
spam_guard_assert(
'Honigtopf ist aus dem Sichtbereich geschoben',
str_contains($field, 'left:-9999px'),
$failures,
$passes
);
spam_guard_assert(
'Honigtopf ist fuer Screenreader und Tastatur unsichtbar',
str_contains($field, 'aria-hidden="true"') && str_contains($field, 'tabindex="-1"'),
$failures,
$passes
);
spam_guard_assert(
'Honigtopf wird nicht von Passwortmanagern befuellt',
str_contains($field, 'autocomplete="off"'),
$failures,
$passes
);
spam_guard_assert(
'Honigtopf ist kein type=hidden (das ueberspringen Bots)',
!str_contains($field, 'type="hidden"'),
$failures,
$passes
);
echo "\n";
if ($failures !== []) {
fwrite(STDERR, 'Spam-Guard-Check fehlgeschlagen: ' . implode('; ', $failures) . "\n");
exit(1);
}
echo "Spam-Guard-Check bestanden mit {$passes} Zusicherungen.\n";