From 748f029afbc91c1fe6b7ee4821cab6c1bc8b3403 Mon Sep 17 00:00:00 2001 From: fs Date: Fri, 31 Jul 2026 17:03:00 +0200 Subject: [PATCH] smtp-test: verdeckte Passwort-Eingabe und Zugangsweg-Sonde MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Auf dem Server scheiterte der Login mit "Could not authenticate", während dieselben Zugangsdaten vom Entwicklungsrechner angenommen werden. Beide Namen (w01ca75d.kasserver.com und der Webserver) zeigen auf dieselbe IP — vom Webspace aus verbindet sich PHP also mit der eigenen Maschine, und die kann Anmeldungen anders behandeln als Verbindungen von außen. --probe testet vier Zugangswege (587/STARTTLS, 465/SSL, localhost:25 mit und ohne Anmeldung) und nennt den ersten, der trägt. Bewusst nur vier: jeder Fehlversuch ist ein fehlgeschlagener Login, zu viele sperren ein Postfach. --frage fragt das Passwort verdeckt ab, damit ein neu gesetztes Postfach-Passwort geprüft werden kann, BEVOR es in eine Datei auf zwei Rechnern wandert. Die Fehlerausgabe nennt bei "Could not authenticate" jetzt die drei Ursachen, die dahinter stecken können. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01NzeVVgMCrzCDJAKeLEdKr7 --- bin/smtp-test.php | 103 +++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 101 insertions(+), 2 deletions(-) diff --git a/bin/smtp-test.php b/bin/smtp-test.php index 097801a..b9a0587 100644 --- a/bin/smtp-test.php +++ b/bin/smtp-test.php @@ -9,7 +9,13 @@ declare(strict_types=1); * NUR CLI — nie im Request-Pfad. Debug-Ausgabe bleibt aus, damit keine * Zugangsdaten (Base64 im AUTH-Dialog) im Terminal oder in Logs landen. * - * Aufruf: php bin/smtp-test.php + * Aufruf: php bin/smtp-test.php (Passwort aus config/config.php) + * php bin/smtp-test.php --frage (Passwort verdeckt eingeben) + * + * --frage ist für den Fall, dass das Postfach-Passwort neu gesetzt wurde: erst + * prüfen, ob es überhaupt trägt, dann in die Config schreiben. Sonst schreibt man + * einen ungetesteten Wert in eine Datei, die auf zwei Rechnern liegt. Verdeckte + * Eingabe statt Argument, damit kein Klartext in der Shell-History landet. */ if (PHP_SAPI !== 'cli') { exit(1); @@ -20,12 +26,90 @@ require dirname(__DIR__) . '/app/bootstrap.php'; use PHPMailer\PHPMailer\PHPMailer; $smtp = config('smtp'); +$frage = in_array('--frage', $argv, true); + +if ($frage) { + echo "Postfach-Passwort für {$smtp['username']} (Eingabe unsichtbar): "; + system('stty -echo'); + $eingabe = trim((string) fgets(STDIN)); + system('stty echo'); + echo "\n"; + if ($eingabe === '') { + fwrite(STDERR, "Abbruch: kein Passwort eingegeben.\n"); + exit(1); + } + $smtp['password'] = $eingabe; +} if (in_array($smtp['password'] ?? '', ['', 'POSTFACH_PASSWORT', 'BREVO_SMTP_KEY', 'CHANGE_ME'], true)) { fwrite(STDERR, "Abbruch: In config/config.php steht noch kein echtes SMTP-Passwort.\n"); + fwrite(STDERR, "Zum Ausprobieren ohne Config-Änderung: php bin/smtp-test.php --frage\n"); exit(1); } +// --- --probe: mehrere Zugangswege durchprobieren --- +// Hintergrund (31.07.2026): dieselben Zugangsdaten wurden vom Entwicklungsrechner +// akzeptiert, vom Webspace aber mit „Could not authenticate" abgelehnt. +// w01ca75d.kasserver.com und der Webserver zeigen auf dieselbe IP — vom Server aus +// verbindet sich PHP also mit der eigenen Maschine, und die kann Anmeldungen anders +// behandeln als von außen. Diese Sonde findet heraus, welcher Weg trägt, statt zu raten. +// Bewusst nur vier Kombinationen: jeder Fehlversuch ist ein fehlgeschlagener Login, +// und zu viele davon können ein Postfach vorübergehend sperren. +if (in_array('--probe', $argv, true)) { + $wege = [ + ['label' => 'Konfiguriert: Host, Port ' . (int) $smtp['port'] . ', STARTTLS, mit Anmeldung', + 'host' => $smtp['host'], 'port' => (int) $smtp['port'], 'sec' => PHPMailer::ENCRYPTION_STARTTLS, 'auth' => true], + ['label' => 'Host, Port 465, SSL, mit Anmeldung', + 'host' => $smtp['host'], 'port' => 465, 'sec' => PHPMailer::ENCRYPTION_SMTPS, 'auth' => true], + ['label' => 'localhost, Port 25, ohne Verschlüsselung, mit Anmeldung', + 'host' => 'localhost', 'port' => 25, 'sec' => '', 'auth' => true], + ['label' => 'localhost, Port 25, ohne Verschlüsselung, OHNE Anmeldung (lokaler Relay)', + 'host' => 'localhost', 'port' => 25, 'sec' => '', 'auth' => false], + ]; + + echo "Sonde: welcher Weg nimmt uns an? Postfach {$smtp['username']}\n\n"; + $erfolg = []; + foreach ($wege as $i => $weg) { + $m = new PHPMailer(true); + $m->isSMTP(); + $m->Host = $weg['host']; + $m->Port = $weg['port']; + $m->SMTPAuth = $weg['auth']; + $m->SMTPSecure = $weg['sec']; + if (!$weg['auth']) { + $m->SMTPAutoTLS = false; + } + $m->Username = $smtp['username']; + $m->Password = $smtp['password']; + $m->Timeout = 12; + try { + $ok = $m->smtpConnect(); + $m->smtpClose(); + } catch (Throwable $e) { + $ok = false; + $m->ErrorInfo = $e->getMessage(); + } + printf(" %d. %-68s %s\n", $i + 1, $weg['label'], $ok ? 'GEHT' : 'nein'); + if (!$ok && $m->ErrorInfo !== '') { + echo ' ' . trim(preg_replace('/\s+/', ' ', $m->ErrorInfo) ?? '') . "\n"; + } + if ($ok) { + $erfolg[] = $weg; + } + } + + echo "\n"; + if ($erfolg === []) { + echo "Kein Weg trägt. Dann liegt es am Postfach selbst: Passwort im KAS neu setzen\n"; + echo "und mit php bin/smtp-test.php --frage gegenprüfen.\n"; + exit(1); + } + $w = $erfolg[0]; + echo "Nimm den ersten Treffer in config/config.php: host {$w['host']}, port {$w['port']}\n"; + echo 'Verschlüsselung ' . ($w['sec'] === '' ? 'keine' : $w['sec']) . ', Anmeldung ' . ($w['auth'] ? 'ja' : 'nein') . "\n"; + exit(0); +} + echo "Teste {$smtp['host']}:{$smtp['port']} (STARTTLS) als {$smtp['username']} …\n"; $mail = new PHPMailer(true); @@ -42,9 +126,24 @@ try { if ($mail->smtpConnect()) { $mail->smtpClose(); echo "OK: Verbindung, STARTTLS und Login erfolgreich — keine Mail versendet.\n"; + if ($frage) { + echo "Dieses Passwort trägt. Jetzt in config/config.php unter smtp.password eintragen.\n"; + } exit(0); } - fwrite(STDERR, 'Fehler: ' . ($mail->ErrorInfo !== '' ? $mail->ErrorInfo : 'Verbindung fehlgeschlagen.') . "\n"); + $info = $mail->ErrorInfo !== '' ? $mail->ErrorInfo : 'Verbindung fehlgeschlagen.'; + fwrite(STDERR, 'Fehler: ' . $info . "\n"); + // „Could not authenticate" heißt: Host, Port, Firewall und STARTTLS sind in + // Ordnung, nur die Zugangsdaten wurden abgelehnt. Das ist die Meldung, die nach + // einem All-Inkl-Tarifwechsel kam (31.07.2026) — dort mussten die + // Postfach-Passwörter im KAS neu gesetzt werden. + if (str_contains($info, 'Could not authenticate')) { + fwrite(STDERR, "\nDie Verbindung stand, abgelehnt wurde nur die Anmeldung. Prüfe:\n"); + fwrite(STDERR, " 1. Postfach-Passwort im KAS neu setzen (nach einem Tarifwechsel nötig).\n"); + fwrite(STDERR, " 2. Stimmt der Postfachname exakt? Aktuell: {$smtp['username']}\n"); + fwrite(STDERR, " 3. Stimmt der Mailserver? Aktuell: {$smtp['host']} (im KAS unter E-Mail nachsehen).\n"); + fwrite(STDERR, " Neues Passwort ohne Config-Änderung ausprobieren: php bin/smtp-test.php --frage\n"); + } exit(1); } catch (Throwable $e) { fwrite(STDERR, 'Fehler: ' . $e->getMessage() . "\n");