'Header-Injection-Test', 'strasse' => 'Musterweg 1', 'plz' => '12345', 'ort' => 'Musterstadt', 'mail_subject_template' => "Rechnung %RNR%\r\nBcc: angreifer@example.com", ]); $this->assertGreaterThan(0, $id, 'Kunde angelegt'); $customer = Customer::find($id); $this->assert( !str_contains($customer['mail_subject_template'], "\r") && !str_contains($customer['mail_subject_template'], "\n"), 'Gespeicherte Betreff-Vorlage enthält kein CR/LF mehr' ); $this->assert( str_contains($customer['mail_subject_template'], 'Bcc: angreifer@example.com'), 'Injizierter Text bleibt als Klartext im Betreff, wird aber nicht mehr als Header interpretierbar' ); // Update mit LF-only-Payload wird ebenfalls bereinigt Customer::update($id, [ 'nachname' => 'Header-Injection-Test', 'strasse' => 'Musterweg 1', 'plz' => '12345', 'ort' => 'Musterstadt', 'mail_subject_template' => "Rechnung %RNR%\nBcc: angreifer2@example.com", ]); $customer = Customer::find($id); $this->assert( !str_contains($customer['mail_subject_template'], "\n"), 'Betreff-Vorlage nach Update ebenfalls ohne LF gespeichert' ); // Defense-in-depth: SmtpMailer::encodeHeader() entfernt CRLF selbst, falls ein // CRLF-Betreff trotzdem irgendwo in den Mailer gelangt (Reflection auf private Methode) $mailer = new SmtpMailer( host: '127.0.0.1', port: 1, encryption: 'none', user: '', pass: '', fromEmail: 'absender@example.com', fromName: 'Absender', ); $ref = new \ReflectionMethod($mailer, 'encodeHeader'); $ref->setAccessible(true); $result = $ref->invoke($mailer, "Rechnung 2026-1\r\nBcc: angreifer@example.com"); $this->assert( !str_contains($result, "\r") && !str_contains($result, "\n"), 'encodeHeader() entfernt CR/LF aus Header-Werten' ); } }