67 lines
2.7 KiB
PHP
67 lines
2.7 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
use Steuerkram\Models\Customer;
|
|
use Steuerkram\Services\SmtpMailer;
|
|
|
|
class MailSubjectHeaderInjectionTest extends TestCase
|
|
{
|
|
protected string $testName = 'Test 55: Header-Injection über mail_subject_template verhindert';
|
|
|
|
public function run(): void
|
|
{
|
|
// CRLF-Payload in der Betreff-Vorlage wird beim Speichern entfernt
|
|
$id = Customer::create([
|
|
'nachname' => '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'
|
|
);
|
|
}
|
|
}
|