Erweiterte Spamschutz-Maßnahmen für Kontaktformular

- Honeypot-Feld zur Bot-Erkennung hinzugefügt
- Zeitbasierte Validierung (min. 3 Sek., max. 1 Std.)
- IP-basiertes Rate-Limiting (3 Anfragen/Stunde)
- Erweiterte Spam-Mustererkennung (URLs, Keywords, etc.)
- Referrer-Validierung auf PHP- und .htaccess-Ebene
- E-Mail-Domain-Prüfung gegen Wegwerf-Adressen
- Nachrichten-Längenvalidierung (10-5000 Zeichen)
- .htaccess-Schutz gegen Bot-User-Agents und verdächtige HTTP-Methoden
This commit is contained in:
2026-02-03 18:17:56 +01:00
parent 11bec81cd0
commit a89c2f693d
4 changed files with 177 additions and 0 deletions

View File

@@ -1,3 +1,14 @@
Version: 4.7.1 (03.02.2026)
- Erweiterte Spamschutz-Maßnahmen für Kontaktformular:
* Honeypot-Feld zur Bot-Erkennung
* Zeitbasierte Validierung (min. 3 Sek., max. 1 Std.)
* IP-basiertes Rate-Limiting (3 Anfragen/Stunde)
* Erweiterte Spam-Mustererkennung (URLs, Keywords, etc.)
* Referrer-Validierung
* E-Mail-Domain-Prüfung gegen Wegwerf-Adressen
* Nachrichten-Längenvalidierung (10-5000 Zeichen)
* .htaccess-Schutz gegen Bots und direkte Aufrufe
Version: 4.7.0
- Updated Bootstrap to version 5.1.3
- Updated all outdated third party vendor libraries to their latest versions

View File

@@ -16,3 +16,25 @@ Options -Indexes
<FilesMatch "\.(bak|backup|old|temp|tmp|swp|~)$">
Require all denied
</FilesMatch>
# Rate-Limiting für contact.php
<IfModule mod_rewrite.c>
RewriteEngine On
# Blockiere bekannte Spam-User-Agents
RewriteCond %{HTTP_USER_AGENT} ^$ [OR]
RewriteCond %{HTTP_USER_AGENT} (bot|crawler|spider|scraper|harvester|curl|wget|python-requests) [NC]
RewriteRule ^contact\.php$ - [F,L]
# Blockiere direkte Aufrufe ohne Referrer (außer von eigener Domain)
RewriteCond %{REQUEST_URI} ^/forms/contact\.php$ [NC]
RewriteCond %{HTTP_REFERER} !^$
RewriteCond %{HTTP_REFERER} !^https?://(www\.)?webfarben\.net [NC]
RewriteRule .* - [F,L]
</IfModule>
# Blockiere verdächtige Request-Methoden
<IfModule mod_rewrite.c>
RewriteCond %{REQUEST_METHOD} ^(HEAD|TRACE|DELETE|TRACK) [NC]
RewriteRule .* - [F,L]
</IfModule>

View File

@@ -24,6 +24,73 @@ if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
exit;
}
// Spam-Schutz: Honeypot-Prüfung
if (!empty($_POST['website'])) {
// Bot hat das Honeypot-Feld ausgefüllt
echo json_encode(['status' => 'error', 'message' => 'Spam erkannt']);
exit;
}
// Spam-Schutz: Zeitbasierte Validierung (mindestens 3 Sekunden)
if (isset($_POST['form_timestamp'])) {
$formTimestamp = intval($_POST['form_timestamp']);
$currentTime = time() * 1000; // Millisekunden
$timeDiff = ($currentTime - $formTimestamp) / 1000; // Sekunden
if ($timeDiff < 3) {
// Formular wurde zu schnell abgeschickt (Bot)
echo json_encode(['status' => 'error', 'message' => 'Bitte warten Sie einen Moment vor dem Absenden']);
exit;
}
if ($timeDiff > 3600) {
// Formular ist zu alt (über 1 Stunde)
echo json_encode(['status' => 'error', 'message' => 'Formular-Session abgelaufen. Bitte laden Sie die Seite neu']);
exit;
}
}
// Spam-Schutz: IP-basiertes Rate-Limiting
$ip = $_SERVER['REMOTE_ADDR'];
$rateLimit = 3; // Maximale Anfragen
$timeWindow = 3600; // Zeitfenster in Sekunden (1 Stunde)
$rateLimitFile = sys_get_temp_dir() . '/contact_rate_limit_' . md5($ip) . '.txt';
if (file_exists($rateLimitFile)) {
$data = json_decode(file_get_contents($rateLimitFile), true);
$attempts = $data['attempts'] ?? 0;
$firstAttempt = $data['first_attempt'] ?? time();
// Prüfe ob Zeitfenster abgelaufen ist
if (time() - $firstAttempt < $timeWindow) {
if ($attempts >= $rateLimit) {
echo json_encode(['status' => 'error', 'message' => 'Zu viele Anfragen. Bitte versuchen Sie es später erneut']);
exit;
}
// Erhöhe Zähler
$data['attempts'] = $attempts + 1;
} else {
// Zeitfenster abgelaufen, reset
$data = ['attempts' => 1, 'first_attempt' => time()];
}
} else {
// Erste Anfrage von dieser IP
$data = ['attempts' => 1, 'first_attempt' => time()];
}
file_put_contents($rateLimitFile, json_encode($data));
// Spam-Schutz: Referrer-Prüfung
if (!empty($_SERVER['HTTP_REFERER'])) {
$referer = parse_url($_SERVER['HTTP_REFERER'], PHP_URL_HOST);
$allowedHosts = ['webfarben.net', 'www.webfarben.net', 'localhost'];
if (!in_array($referer, $allowedHosts)) {
echo json_encode(['status' => 'error', 'message' => 'Ungültige Anfrage-Quelle']);
exit;
}
}
// POST-Daten prüfen und bereinigen
$name = isset($_POST['name']) ? trim(htmlspecialchars($_POST['name'])) : '';
$email = isset($_POST['email']) ? trim($_POST['email']) : '';
@@ -49,6 +116,72 @@ if (empty($message)) {
$errors[] = 'Nachricht ist erforderlich';
}
// Spam-Schutz: Inhaltsvalidierung
function containsSpamPatterns($text) {
// Prüfe auf zu viele URLs
$urlCount = preg_match_all('/https?:\/\/[^\s]+/i', $text, $matches);
if ($urlCount > 2) {
return true;
}
// Prüfe auf verdächtige Spam-Begriffe
$spamKeywords = [
'viagra', 'cialis', 'casino', 'poker', 'lottery',
'crypto', 'bitcoin', 'investment', 'forex', 'loan',
'weight loss', 'diet pills', 'replica', 'rolex',
'click here', 'buy now', 'limited offer', 'act now',
'congratulations', 'you won', 'claim your prize',
'enlargement', 'pharmacy', 'prescription'
];
$lowerText = strtolower($text);
foreach ($spamKeywords as $keyword) {
if (strpos($lowerText, $keyword) !== false) {
return true;
}
}
// Prüfe auf zu viele Großbuchstaben (mehr als 50%)
$upperCount = preg_match_all('/[A-Z]/', $text);
$letterCount = preg_match_all('/[A-Za-z]/', $text);
if ($letterCount > 10 && ($upperCount / $letterCount) > 0.5) {
return true;
}
// Prüfe auf verdächtig viele gleiche Zeichen hintereinander
if (preg_match('/(.)\1{10,}/', $text)) {
return true;
}
return false;
}
// Validiere Nachricht und Betreff auf Spam-Muster
if (containsSpamPatterns($message) || containsSpamPatterns($subject)) {
$errors[] = 'Ihre Nachricht enthält unzulässige Inhalte';
}
// Prüfe auf verdächtige E-Mail-Domains
$emailDomain = substr(strrchr($email, "@"), 1);
$suspiciousDomains = [
'tempmail.com', 'throwaway.email', '10minutemail.com',
'guerrillamail.com', 'mailinator.com', 'maildrop.cc'
];
if (in_array(strtolower($emailDomain), $suspiciousDomains)) {
$errors[] = 'Bitte verwenden Sie eine gültige E-Mail-Adresse';
}
// Prüfe auf minimale Nachrichtenlänge
if (strlen($message) < 10) {
$errors[] = 'Die Nachricht ist zu kurz (mindestens 10 Zeichen)';
}
// Prüfe auf maximale Nachrichtenlänge
if (strlen($message) > 5000) {
$errors[] = 'Die Nachricht ist zu lang (maximal 5000 Zeichen)';
}
if (!empty($errors)) {
echo json_encode(['status' => 'error', 'message' => implode(', ', $errors)]);
exit;

View File

@@ -716,6 +716,10 @@
<div class="col-lg-8 mt-5 mt-lg-0">
<form action="forms/contact.php" method="post" role="form" class="php-email-form">
<!-- Honeypot-Feld (versteckt, nur für Bots sichtbar) -->
<input type="text" name="website" style="display:none !important" tabindex="-1" autocomplete="off">
<!-- Zeitstempel für Spam-Schutz -->
<input type="hidden" name="form_timestamp" value="">
<div class="row">
<div class="col-md-6 form-group">
<input type="text" name="name" class="form-control" id="name" placeholder="Your Name" required>
@@ -738,6 +742,13 @@
<div class="text-center"><button type="submit">Send Message</button></div>
</form>
<script>
// Setze Zeitstempel wenn Formular geladen wird
document.addEventListener('DOMContentLoaded', function() {
document.querySelector('input[name="form_timestamp"]').value = Date.now();
});
</script>
</div>
</div>