Files
landingpage-haus-schleusingen/app/controllers/HomeController.php
Claw (KI-Assistent) a919a392cc
All checks were successful
Deploy Feature Branch to Test / deploy (push) Successful in 25s
Lint / PHP Syntax Check (push) Successful in 32s
Lint / CSS Lint (stylelint) (push) Successful in 1m13s
Lint / HTML Lint (htmlhint) (push) Successful in 1m9s
Lint / PHP Syntax Check (pull_request) Successful in 32s
Lint / CSS Lint (stylelint) (pull_request) Successful in 1m16s
Lint / HTML Lint (htmlhint) (pull_request) Successful in 1m7s
fix(security): add CSRF protection to contact form (#42)
- Generate CSRF token (32 bytes) on GET requests
- Add hidden csrf_token field to contact form
- Validate token with hash_equals() (timing-safe) on POST
- Reject invalid/missing tokens with user-friendly error

Fix #42
2026-05-21 23:05:51 +00:00

188 lines
8.7 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php
declare(strict_types=1);
namespace App\Controllers;
class HomeController extends Controller
{
public function index(): void
{
session_start();
// --- Helper functions ---
$normalizeContactValue = function (string $value): string {
return trim($value);
};
$escapeContactValue = function (string $value): string {
return htmlspecialchars($value, ENT_QUOTES, 'UTF-8');
};
$containsHeaderInjection = function (string $value): bool {
return (bool) preg_match('/[\r\n]/', $value);
};
// --- Form processing ---
$formErrors = [];
$formSuccess = false;
if (!empty($_SESSION['form_success'])) {
$formSuccess = true;
unset($_SESSION['form_success']);
}
if (!empty($_SESSION['form_errors'])) {
$formErrors = $_SESSION['form_errors'];
unset($_SESSION['form_errors']);
}
if (!empty($_SESSION['form_data'])) {
$formData = $_SESSION['form_data'];
unset($_SESSION['form_data']);
} else {
$formData = ['fname' => '', 'lname' => '', 'email' => '', 'phone' => '', 'interest' => 'Besichtigung anfragen', 'message' => ''];
}
// CSRF-Token generieren (nach Session-Start)
if (empty($_SESSION['csrf_token'])) {
$_SESSION['csrf_token'] = bin2hex(random_bytes(32));
}
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
// CSRF-Token validieren
$csrfToken = $_POST['csrf_token'] ?? '';
if (!hash_equals($_SESSION['csrf_token'] ?? '', $csrfToken)) {
header('Location: /#form-result');
$_SESSION['form_errors'] = ['Sicherheitsüberprüfung fehlgeschlagen. Bitte versuchen Sie es erneut.'];
exit;
}
$formData['fname'] = $normalizeContactValue((string) ($_POST['fname'] ?? ''));
$formData['lname'] = $normalizeContactValue((string) ($_POST['lname'] ?? ''));
$formData['email'] = $normalizeContactValue((string) ($_POST['email'] ?? ''));
$formData['phone'] = $normalizeContactValue((string) ($_POST['phone'] ?? ''));
$formData['interest'] = $normalizeContactValue((string) ($_POST['interest'] ?? ''));
$formData['message'] = $normalizeContactValue((string) ($_POST['message'] ?? ''));
$honeypot = $normalizeContactValue((string) ($_POST['website'] ?? ''));
if ($honeypot !== '') {
header('Location: ' . $_SERVER['REQUEST_URI'] . '#form-result');
$_SESSION['form_success'] = true;
exit;
} else {
if ($formData['fname'] === '') {
$formErrors[] = 'Bitte geben Sie Ihren Vornamen an.';
}
if ($formData['lname'] === '') {
$formErrors[] = 'Bitte geben Sie Ihren Nachnamen an.';
}
if ($formData['email'] === '' || !filter_var($formData['email'], FILTER_VALIDATE_EMAIL)) {
$formErrors[] = 'Bitte geben Sie eine gültige E-Mail-Adresse an.';
}
if ($formData['message'] === '') {
$formErrors[] = 'Bitte geben Sie eine Nachricht ein.';
}
if ($containsHeaderInjection($formData['email']) || $containsHeaderInjection($formData['fname'] . ' ' . $formData['lname'])) {
$formErrors[] = 'Ungültige Zeichen in den Eingabefeldern.';
}
$formTime = isset($_POST['form_time']) ? (int) $_POST['form_time'] : 0;
if ($formTime > 0 && (time() - $formTime) < 3) {
$formErrors[] = 'Das Formular wurde zu schnell abgeschickt. Bitte versuchen Sie es erneut.';
}
$lastSubmit = $_SESSION['last_contact_submit'] ?? 0;
if ($lastSubmit && (time() - $lastSubmit) < 60) {
$formErrors[] = 'Bitte warten Sie einen Moment vor der nächsten Anfrage.';
}
if (empty($formErrors)) {
$to = 'mki@kies-media.de';
$subject = 'Kontaktanfrage: ' . $formData['interest'];
$body = "Von: {$formData['fname']} {$formData['lname']}\n"
. "E-Mail: {$formData['email']}\n";
if ($formData['phone'] !== '') {
$body .= "Telefon: {$formData['phone']}\n";
}
$body .= "Anliegen: {$formData['interest']}\n\n"
. $formData['message'];
$headers = "From: {$formData['email']}\r\n";
$headers .= "Reply-To: {$formData['email']}\r\n";
$headers .= "Content-Type: text/plain; charset=UTF-8\r\n";
$headers .= "X-Mailer: PHP/" . phpversion();
$mailSent = mail($to, $subject, $body, $headers);
if ($mailSent) {
$_SESSION['last_contact_submit'] = time();
header('Location: ' . $_SERVER['REQUEST_URI'] . '#form-result');
$_SESSION['form_success'] = true;
exit;
} else {
$formErrors[] = 'Leider konnte die E-Mail nicht gesendet werden. Bitte versuchen Sie es später erneut oder schreiben Sie uns direkt an mki@kies-media.de.';
}
}
}
if (!empty($formErrors)) {
header('Location: ' . $_SERVER['REQUEST_URI'] . '#form-result');
$_SESSION['form_errors'] = $formErrors;
$_SESSION['form_data'] = $formData;
exit;
}
}
$this->render('home/index', [
'formSuccess' => $formSuccess,
'formErrors' => $formErrors,
'formData' => $formData,
'escapeContactValue' => $escapeContactValue,
'pageTitle' => 'Einfamilienhaus mieten Schleusingen | 227 m², 6 Zimmer | 1.300 € Kaltmiete',
'pageDescription' => 'Einfamilienhaus zur Langzeitmiete in Schleusingen: 227 m² Wohnfläche, 6 Zimmer, 3 Etagen mit Dachterrasse. Kaltmiete 1.300 €. Bahnhofstraße 10, 98553 Schleusingen. Ab sofort verfügbar.',
'canonical' => 'https://haus-schleusingen.de/',
'openGraph' => [
'ogTitle' => 'Einfamilienhaus zur Miete in Schleusingen 227 m², 6 Zimmer',
'ogDescription' => 'Großzügiges Einfamilienhaus zur Langzeitmiete: 227 m², 6 Zimmer, 3 Etagen + Dachterrasse. Kaltmiete 1.300 €. Ab sofort verfügbar in Schleusingen.',
'ogImage' => 'https://haus-schleusingen.de/bilder/Außenansicht-2.png',
'ogUrl' => 'https://haus-schleusingen.de/',
],
'structuredData' => json_encode([
'@context' => 'https://schema.org',
'@type' => 'RealEstateListing',
'name' => 'Einfamilienhaus zur Miete in Schleusingen',
'description' => 'Großzügiges Einfamilienhaus zur Langzeitmiete: 227 m² Wohnfläche, 6 Zimmer, 3 Etagen mit Dachterrasse. Kaltmiete 1.300 €.',
'url' => 'https://haus-schleusingen.de/',
'image' => 'https://haus-schleusingen.de/bilder/Außenansicht-2.png',
'datePosted' => '2026-05-14',
'address' => [
'@type' => 'PostalAddress',
'streetAddress' => 'Bahnhofstraße 10',
'addressLocality' => 'Schleusingen',
'postalCode' => '98553',
'addressCountry' => 'DE',
],
'offers' => [
'@type' => 'Offer',
'price' => '1300',
'priceCurrency' => 'EUR',
'priceSpecification' => [
'@type' => 'UnitPriceSpecification',
'price' => '1300',
'priceCurrency' => 'EUR',
'unitCode' => 'MON',
'description' => 'Kaltmiete pro Monat',
],
],
'floorSize' => [
'@type' => 'QuantitativeValue',
'value' => '227',
'unitCode' => 'MTK',
],
'numberOfRooms' => [
'@type' => 'QuantitativeValue',
'value' => '6',
],
]),
]);
}
}