→ Formular mit vorausgefüllten Feldern (Convenience aus Bestell-Mail) * POST → Widerruf erfassen, Bestätigungsseite anzeigen * * Submit läuft IMMER über Bestellnr + E-Mail Lookup, nicht über Token. * * @copyright Wlanium / Thomas Bartelt * @since 2026-04-19 */ session_start(); ini_set('display_errors', 0); error_reporting(0); ini_set('log_errors', 1); ini_set('error_log', __DIR__.'/error_log.txt'); header('X-Robots-Tag: noindex, nofollow', true); header('Content-Security-Policy: default-src \'self\'; style-src \'unsafe-inline\''); include_once './core/base.class.php'; include_once './core/registry.class.php'; $base_object = new base(); Registry::set('base', $base_object); include_once './core/revocation.class.php'; $revocation = new Revocation($base_object); $view = 'form'; $prefill = ['name' => '', 'email' => '', 'order_number' => '', 'article' => '']; $error = null; // ------------------------------------------------------------ // Tor-Exit-Liste — wird per Cron alle 4h aktualisiert. // Datei in /var/cache/tor/exit-list.txt, eine IP pro Zeile. // ------------------------------------------------------------ function widerruf_is_tor_exit($ip) { static $set = null; if ($set === null) { $f = '/var/cache/tor/exit-list.txt'; if (file_exists($f) && (time() - filemtime($f)) < 172800) { $arr = @file($f, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES); $set = $arr ? array_flip(array_map('trim', $arr)) : []; } else { $set = []; // fail-open wenn Liste fehlt/zu alt } } return $ip !== '' && isset($set[$ip]); } $client_ip = $_SERVER['REMOTE_ADDR'] ?? ''; $is_tor = widerruf_is_tor_exit($client_ip); // ------------------------------------------------------------ // Request dispatch // ------------------------------------------------------------ if ($_SERVER['REQUEST_METHOD'] === 'POST') { $ip_now = $_SERVER['REMOTE_ADDR'] ?? ''; $ua_now = $_SERVER['HTTP_USER_AGENT'] ?? ''; // ------------------------------------------------------------ // Bot-Schutz: tor-exit + honeypot + time-trap + session-token + rate-limit. // Bei Bot-Verdacht still "success" zurückgeben (Bot soll nichts merken). // ------------------------------------------------------------ $is_bot = false; $bot_reason = ''; if ($is_tor) { $is_bot = true; $bot_reason = 'tor-exit'; } if (!$is_bot && (!empty($_POST['website']) || !empty($_POST['fax']))) { $is_bot = true; $bot_reason = 'honeypot'; } if (!$is_bot) { $ts = isset($_POST['ts']) ? (int)$_POST['ts'] : 0; $delta = time() - $ts; if ($ts <= 0 || $delta < 4 || $delta > 7200) { $is_bot = true; $bot_reason = 'timetrap delta='.$delta; } } if (!$is_bot) { if (empty($_POST['form_token']) || empty($_SESSION['widerruf_token']) || !hash_equals((string)$_SESSION['widerruf_token'], (string)$_POST['form_token'])) { $is_bot = true; $bot_reason = 'token-mismatch'; } } if (!$is_bot) { $rl_hash = sha1($ip_now.'|widerruf'); $rl_file = sys_get_temp_dir().'/widerruf_rl_'.$rl_hash; $last = file_exists($rl_file) ? (int)@file_get_contents($rl_file) : 0; if (time() - $last < 60) { $is_bot = true; $bot_reason = 'ratelimit last='.$last; } else { @file_put_contents($rl_file, (string)time()); } } if ($is_bot) { error_log('[widerruf] BOT BLOCKED reason='.$bot_reason.' ip='.$ip_now.' ua='.substr($ua_now, 0, 120)); // Token einmalig verbrauchen, damit derselbe nicht reusable ist unset($_SESSION['widerruf_token']); $view = 'success'; } else { // Token verbrauchen unset($_SESSION['widerruf_token']); $form_name = isset($_POST['name']) ? trim((string)$_POST['name']) : ''; $form_email = isset($_POST['email']) ? trim((string)$_POST['email']) : ''; $form_order_number = isset($_POST['order_number']) ? trim((string)$_POST['order_number']) : ''; $form_article = isset($_POST['article']) ? trim((string)$_POST['article']) : ''; $prefill = [ 'name' => $form_name, 'email' => $form_email, 'order_number' => $form_order_number, 'article' => $form_article, ]; if ($revocation->submit_anonymous( $form_name, $form_order_number, $form_email, $form_article, $ip_now, $ua_now )) { $view = 'success'; } else { $view = 'form'; $error = $revocation->get_error(); } } } else { // GET — Tor-Exit-Nutzer:innen sehen statt Form einen Hinweis auf alternative Wege if ($is_tor) { error_log('[widerruf] TOR EXIT GET ip='.$client_ip); $view = 'tor_blocked'; } else { // Bei Token-Link: Felder vorausfüllen $token = isset($_GET['t']) ? (string)$_GET['t'] : ''; if ($token !== '') { $pf = $revocation->prefill_from_token($token); if ($pf) { $prefill = $pf; } elseif ($revocation->get_error() === 'feature_disabled') { $view = 'error'; $error = 'feature_disabled'; } // Bei anderen Token-Fehlern: leeres Formular anbieten ohne Hinweistext } } } // Neuen Form-Token + Timestamp für Render erzeugen if ($view === 'form') { $_SESSION['widerruf_token'] = bin2hex(random_bytes(16)); $form_token_render = $_SESSION['widerruf_token']; $form_ts_render = time(); } $reasons = $revocation->fetch_reasons(); $messages = [ 'missing_name' => 'Bitte geben Sie Ihren Namen an.', 'invalid_email' => 'Bitte geben Sie eine gültige E-Mail-Adresse an.', 'already_submitted' => 'Für diese Bestellung wurde bereits ein Widerruf erklärt.', 'feature_disabled' => 'Die elektronische Widerrufsfunktion ist aktuell noch nicht aktiv.', ]; $display_error = isset($messages[$error]) ? $messages[$error] : null; ?>
Gemäß § 355 BGB haben Sie das Recht, Ihren Vertrag binnen 14 Tagen ohne Angabe von Gründen zu widerrufen. Pflichtfelder sind Name und E-Mail-Adresse. Bestellnummer und Artikel sind freiwillige Angaben, die uns die Zuordnung erleichtern.
Sie scheinen über eine anonyme Verbindung (z. B. Tor) auf diese Seite zuzugreifen. Aus Spam-Schutz-Gründen ist das Online-Formular über anonyme Netzwerke deaktiviert.
Ihr Widerrufsrecht bleibt selbstverständlich unverändert. Sie können Ihren Widerruf jederzeit auf einem der folgenden Wege erklären:
= htmlspecialchars(REVOCATION_RETURN_COMPANY) ?> · = htmlspecialchars(REVOCATION_RETURN_STREET) ?> · = htmlspecialchars(REVOCATION_RETURN_CITY) ?>
Bei Rückfragen erreichen Sie uns unter = htmlspecialchars(REVOCATION_OWNER_EMAIL) ?>.
Sie können Ihren Widerruf jederzeit auch formlos per E-Mail an = htmlspecialchars(REVOCATION_OWNER_EMAIL) ?> oder per Brief erklären.
= htmlspecialchars(REVOCATION_RETURN_COMPANY) ?> · = htmlspecialchars(REVOCATION_RETURN_STREET) ?> · = htmlspecialchars(REVOCATION_RETURN_CITY) ?>