= RATE_LIMIT_MAX && ($now - $data['timestamp']) < RATE_LIMIT_WINDOW) { return false; } if (($now - $data['timestamp']) >= RATE_LIMIT_WINDOW) { $data = ['count' => 1, 'timestamp' => $now]; } else { $data['count']++; } } else { $data = ['count' => 1, 'timestamp' => time()]; } file_put_contents($file, json_encode($data), LOCK_EX); return true; } // Erweiterte Validierung mit XSS-Schutz function validateInput($data) { // Leere Felder prüfen if (empty($data['name']) || empty($data['email']) || empty($data['message'])) { return ['valid' => false, 'message' => 'Bitte füllen Sie alle Pflichtfelder aus.']; } // E-Mail-Format prüfen if (!filter_var($data['email'], FILTER_VALIDATE_EMAIL)) { return ['valid' => false, 'message' => 'Bitte geben Sie eine gültige E-Mail-Adresse ein.']; } // Zusätzliche E-Mail-Validierung (keine Header-Injection) if (preg_match('/[\r\n]/', $data['email']) || preg_match('/[\r\n]/', $data['name'])) { return ['valid' => false, 'message' => 'Ungültige Zeichen erkannt.']; } // Längenbeschränkungen if (strlen($data['name']) > 100) { return ['valid' => false, 'message' => 'Der Name ist zu lang.']; } if (strlen($data['message']) < 10) { return ['valid' => false, 'message' => 'Ihre Nachricht ist zu kurz.']; } if (strlen($data['message']) > 5000) { return ['valid' => false, 'message' => 'Ihre Nachricht ist zu lang.']; } // Spam-Wörter aus Config prüfen foreach (SPAM_WORDS as $word) { if (stripos($data['message'], $word) !== false || stripos($data['name'], $word) !== false) { return ['valid' => false, 'message' => 'Ihre Nachricht wurde als Spam erkannt.']; } } return ['valid' => true]; } // XSS-Schutz für Ausgaben function sanitizeOutput($string) { return htmlspecialchars($string, ENT_QUOTES, 'UTF-8'); } // reCAPTCHA Validierung mit Config-Konstante function validateRecaptcha($recaptchaResponse) { if (empty($recaptchaResponse)) { return false; } $secret = RECAPTCHA_SECRET; $url = 'https://www.google.com/recaptcha/api/siteverify'; $data = [ 'secret' => $secret, 'response' => $recaptchaResponse, 'remoteip' => $_SERVER['REMOTE_ADDR'] ]; $options = [ 'http' => [ 'header' => "Content-type: application/x-www-form-urlencoded\r\n", 'method' => 'POST', 'content' => http_build_query($data), 'timeout' => 10 ], 'ssl' => [ 'verify_peer' => true, 'verify_peer_name' => true, ] ]; $context = stream_context_create($options); $result = @file_get_contents($url, false, $context); if ($result === false) { error_log('reCAPTCHA API request failed'); return false; } $responseData = json_decode($result); return isset($responseData->success) && $responseData->success === true; } // Honeypot-Feld prüfen if (!empty($_POST['website'])) { echo json_encode([ 'success' => false, 'message' => 'Spam erkannt.' ]); exit; } // Zeitbasierte Validierung mit Config-Konstante if (isset($_POST['form_start'])) { $delta = time() - intval($_POST['form_start'] / 1000); if ($delta < MIN_FORM_TIME) { // Zu schnell ausgefüllt echo json_encode([ 'success' => false, 'message' => 'Zu schnell abgesendet. Bitte versuchen Sie es erneut.' ]); exit; } } // Hauptlogik header('Content-Type: application/json'); if ($_SERVER['REQUEST_METHOD'] !== 'POST') { echo json_encode(['success' => false, 'message' => 'Nur POST-Requests erlaubt']); exit; } try { // Rate Limit prüfen if (!checkRateLimit()) { echo json_encode([ 'success' => false, 'message' => 'Aus Sicherheitsgründen können Sie nur 5 Nachrichten pro Stunde senden. Bitte versuchen Sie es später erneut.' ]); exit; } // reCAPTCHA prüfen $recaptchaResponse = $_POST['g-recaptcha-response'] ?? ''; if (!validateRecaptcha($recaptchaResponse)) { echo json_encode([ 'success' => false, 'message' => 'Bitte bestätigen Sie, dass Sie kein Robot sind.' ]); exit; } // Formulardaten sammeln und bereinigen $formData = [ 'name' => trim($_POST['name'] ?? ''), 'email' => trim($_POST['email'] ?? ''), 'mobile' => trim($_POST['mobilenumber'] ?? ''), 'message' => trim($_POST['message'] ?? '') ]; // E-Mail-Domain-Blacklist aus Config foreach (SPAM_DOMAINS as $domain) { if (stripos($formData['email'], $domain) !== false) { echo json_encode([ 'success' => false, 'message' => 'Bitte verwenden Sie eine echte E-Mail-Adresse.' ]); exit; } } // Eingaben validieren $validation = validateInput($formData); if (!$validation['valid']) { echo json_encode([ 'success' => false, 'message' => $validation['message'] ]); exit; } // E-Mail erstellen $contact = new PHP_Email_Form; $contact->ajax = true; $contact->to = RECEIVING_EMAIL; $contact->from_name = $formData['name']; $contact->from_email = 'hkw@webfarben.net'; $contact->reply_to = $formData['email']; $contact->subject = "Neue Kontaktanfrage von " . $formData['name']; $contact->smtp = SMTP_CONFIG; // Nachricht zusammenbauen $contact->add_message($formData['name'], 'Name'); $contact->add_message($formData['email'], 'E-Mail'); $contact->add_message($formData['mobile'], 'Telefon'); $contact->add_message($formData['message'], 'Nachricht'); // Dateianhang verarbeiten mit Sicherheitschecks if (isset($_FILES['resume']) && $_FILES['resume']['error'] == UPLOAD_ERR_OK) { // Dateigröße prüfen if ($_FILES['resume']['size'] > MAX_FILE_SIZE) { echo json_encode([ 'success' => false, 'message' => 'Die Datei ist zu groß (max. 20 MB).' ]); exit; } $contact->add_attachment('resume', 20, ALLOWED_FILE_TYPES); } // E-Mail senden $result = $contact->send(); echo json_encode([ 'success' => ($result === 'OK'), 'message' => ($result === 'OK') ? "Ihre Nachricht wurde erfolgreich gesendet an: " . RECEIVING_EMAIL : "Fehler beim Senden: " . $result ]); } catch (Exception $e) { error_log("Mailer Error: " . $e->getMessage()); echo json_encode([ 'success' => false, 'message' => "Ein Fehler ist aufgetreten: " . $e->getMessage() ]); }