mivita/app/Services/TurnstileVerifier.php
Kevin Adametz 4c944aa15d Spamschutz Kontakt + Registrierung: Turnstile, Rate-Limit, Honeypot; Passwort min. 8
- Kontaktformular: keine Mail mehr an die eingegebene Adresse (wurde als
  Spam-Versender missbraucht); Anfrage nur an Kontakt-Adresse + Berater (BCC),
  Reply-To = Absender; Eingaben in der Mail escaped
- Kontakt + Registrierung: Cloudflare Turnstile statt reCAPTCHA (Schluessel
  aus dem Code in die .env), Rate-Limits, Honeypot + Mindest-Ausfuellzeit,
  Namensregel (lateinische Schrift, max. 50 Zeichen), Form Requests
- Registrierung: Partner-Nummer nur fuer aktive Berater (vorher ungeprueft)
- CRM: POST /register abgeschaltet (legte ohne Captcha Benutzer an)
- Passwort-Mindestlaenge systemweit 8 Zeichen ueber Password::defaults()
- Log abgefangener Anfragen: storage/logs/form-spam-*.log
- Doku und Protokoll: dev/2026-10-09-spam/README.md

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 08:22:02 +00:00

46 lines
1.3 KiB
PHP

<?php
namespace App\Services;
use Illuminate\Http\Client\ConnectionException;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Log;
/**
* Prüft Cloudflare-Turnstile-Tokens (kostenloses Captcha) serverseitig.
*
* Schlägt die Prüfung technisch fehl (Cloudflare nicht erreichbar, kein Secret),
* gilt das Token als ungültig.
*/
class TurnstileVerifier
{
public const VERIFY_URL = 'https://challenges.cloudflare.com/turnstile/v0/siteverify';
public function verify(?string $token, ?string $ip = null): bool
{
if (blank($token)) {
return false;
}
$secret = config('services.turnstile.secret_key');
if (blank($secret)) {
Log::channel('form_spam')->error('Turnstile: TURNSTILE_SECRET_KEY ist nicht gesetzt.');
return false;
}
try {
$response = Http::asForm()->timeout(5)->post(self::VERIFY_URL, [
'secret' => $secret,
'response' => $token,
'remoteip' => $ip,
]);
} catch (ConnectionException $e) {
Log::channel('form_spam')->warning('Turnstile: Cloudflare nicht erreichbar.', ['error' => $e->getMessage()]);
return false;
}
return $response->successful() && $response->json('success') === true;
}
}