fix: spam-friendly mail headers; docs: monitor.php token todo
Use MTA-style Message-ID and display names on From/To to avoid SpamAssassin MSGID_RANDY and PDS_NAKED_TO_NUMERO. Add todo/12 for HTTP token gate on monitor.php (CLI cron unchanged). Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
+39
-4
@@ -44,7 +44,7 @@ function sendMail(string $to, string $subject, string $body, ?string $from = nul
|
|||||||
sendCommand($socket, base64_encode($pass), 235);
|
sendCommand($socket, base64_encode($pass), 235);
|
||||||
|
|
||||||
sendCommand($socket, 'MAIL FROM:<' . $envelopeFrom . '>', 250);
|
sendCommand($socket, 'MAIL FROM:<' . $envelopeFrom . '>', 250);
|
||||||
sendCommand($socket, 'RCPT TO:<' . $to . '>', 250);
|
sendCommand($socket, 'RCPT TO:<' . bareMailAddress($to) . '>', 250);
|
||||||
sendCommand($socket, 'DATA', 354);
|
sendCommand($socket, 'DATA', 354);
|
||||||
|
|
||||||
$message = buildMessage($fromHeader, $to, $subject, $body);
|
$message = buildMessage($fromHeader, $to, $subject, $body);
|
||||||
@@ -115,15 +115,50 @@ function readResponse($socket): string
|
|||||||
return trim($response);
|
return trim($response);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function bareMailAddress(string $address): string
|
||||||
|
{
|
||||||
|
$address = trim($address);
|
||||||
|
|
||||||
|
if (preg_match('/<([^>]+)>/', $address, $matches)) {
|
||||||
|
return trim($matches[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $address;
|
||||||
|
}
|
||||||
|
|
||||||
|
function encodeDisplayName(string $name): string
|
||||||
|
{
|
||||||
|
if (preg_match('/[\r\n"]/', $name)) {
|
||||||
|
throw new InvalidArgumentException('Invalid display name');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (preg_match('/^[A-Za-z0-9 !\'.\-]+$/', $name)) {
|
||||||
|
return $name;
|
||||||
|
}
|
||||||
|
|
||||||
|
return '"' . str_replace(['\\', '"'], ['\\\\', '\\"'], $name) . '"';
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatMailAddress(string $email, string $displayName): string
|
||||||
|
{
|
||||||
|
$email = bareMailAddress($email);
|
||||||
|
|
||||||
|
return encodeDisplayName($displayName) . ' <' . $email . '>';
|
||||||
|
}
|
||||||
|
|
||||||
function buildMessage(string $from, string $to, string $subject, string $body): string
|
function buildMessage(string $from, string $to, string $subject, string $body): string
|
||||||
{
|
{
|
||||||
$domain = env('DOMAIN', '0012.de');
|
$domain = env('DOMAIN', '0012.de');
|
||||||
$date = gmdate('D, d M Y H:i:s') . ' +0000';
|
$date = gmdate('D, d M Y H:i:s') . ' +0000';
|
||||||
$messageId = sprintf('<%s@%s>', bin2hex(random_bytes(8)), $domain);
|
// MTA-style ID (time + pid) — avoids SpamAssassin MSGID_RANDY on random hex IDs.
|
||||||
|
$messageId = sprintf('<%s.%s@%s>', gmdate('YmdHis'), getmypid(), $domain);
|
||||||
|
|
||||||
|
$fromHeader = formatMailAddress($from, env('SMTP_FROM_NAME', '0012 Monitoring'));
|
||||||
|
$toHeader = formatMailAddress($to, env('MAIL_TO_NAME', 'Alert Recipient'));
|
||||||
|
|
||||||
$headers = [
|
$headers = [
|
||||||
"From: {$from}",
|
"From: {$fromHeader}",
|
||||||
"To: {$to}",
|
"To: {$toHeader}",
|
||||||
"Subject: {$subject}",
|
"Subject: {$subject}",
|
||||||
"Date: {$date}",
|
"Date: {$date}",
|
||||||
"Message-ID: {$messageId}",
|
"Message-ID: {$messageId}",
|
||||||
|
|||||||
@@ -0,0 +1,44 @@
|
|||||||
|
# 12 — monitor.php: Token-Check (HTTP)
|
||||||
|
|
||||||
|
**Priorität:** medium
|
||||||
|
**Abhängigkeiten:** 03 (monitor.php), 05 (Plesk-Cron)
|
||||||
|
**Referenz:** [`../public/monitor.php`](../public/monitor.php), [`../public/mail-test.php`](../public/mail-test.php)
|
||||||
|
|
||||||
|
## Problem
|
||||||
|
|
||||||
|
`monitor.php` ist per **GET** öffentlich erreichbar (`https://0012.de/monitor.php`). Im Plesk-Access-Log tauchen neben dem Cron-`curl` auch fremde Clients auf (z. B. `got` von AWS-IPs) — jeder kann Checks auslösen, JSON/Status lesen und Last erzeugen.
|
||||||
|
|
||||||
|
Plesk-Cron kann das Skript **per CLI** ausführen (`php monitor.php`) — dort ist kein HTTP nötig.
|
||||||
|
|
||||||
|
## Ziel
|
||||||
|
|
||||||
|
HTTP-Aufrufe nur mit gültigem Token; CLI/Cron unverändert.
|
||||||
|
|
||||||
|
## Checkliste
|
||||||
|
|
||||||
|
- [ ] In `monitor.php` (oder `monitor-lib.php`): bei Web-SAPI (`php_sapi_name() !== 'cli'`) Token prüfen
|
||||||
|
- Query-Parameter `token` oder Header `X-Monitor-Token`
|
||||||
|
- Secret: bestehendes `MONITOR_INGEST_TOKEN` (wie `mail-test.php`) oder separates `MONITOR_RUN_TOKEN`
|
||||||
|
- Vergleich mit `hash_equals()`
|
||||||
|
- Bei Fehler: `403` + kurzer JSON/Text, **kein** Monitoring-Lauf
|
||||||
|
- [ ] Plesk-Cron auf **PHP-Skript (CLI)** umstellen, falls aktuell URL/Curl — siehe [`05-plesk-cron.md`](05-plesk-cron.md)
|
||||||
|
- [ ] Optional: `.htaccess` / Plesk „Verzeichnis schützen“ nur als Zusatz, nicht als einzige Absicherung
|
||||||
|
- [ ] Deploy + manueller Test:
|
||||||
|
- `GET /monitor.php` ohne Token → 403
|
||||||
|
- `GET /monitor.php?token=…` → Lauf OK
|
||||||
|
- `php monitor.php` lokal/auf Server → Lauf OK
|
||||||
|
- [ ] Kurz in [`../MONITORING.md`](../MONITORING.md) oder [`05-plesk-cron.md`](05-plesk-cron.md) dokumentieren
|
||||||
|
|
||||||
|
## Nicht im Scope
|
||||||
|
|
||||||
|
- Rate-Limit / IP-Whitelist (optional später)
|
||||||
|
- Absicherung von `index.php` (öffentliche Statusseite bleibt bewusst offen)
|
||||||
|
|
||||||
|
## Akzeptanzkriterien
|
||||||
|
|
||||||
|
- Fremde GET-Requests ohne Token lösen keinen Monitor-Lauf aus
|
||||||
|
- Geplanter Plesk-Cron (CLI) läuft weiter zuverlässig
|
||||||
|
|
||||||
|
## Referenz-Implementierung
|
||||||
|
|
||||||
|
Vorbild Token-Check: [`../public/mail-test.php`](../public/mail-test.php) (Zeilen 15–22).
|
||||||
@@ -15,6 +15,7 @@ Nummerierte Todos für den 0012.de-Webspace. Abhängigkeiten beachten.
|
|||||||
| 09 | [09-monitoring-phase3.md](09-monitoring-phase3.md) | **low** | Rest-Domains, Dashboard, Header-Checks |
|
| 09 | [09-monitoring-phase3.md](09-monitoring-phase3.md) | **low** | Rest-Domains, Dashboard, Header-Checks |
|
||||||
| 10 | [10-optional-plesk-git-composer.md](10-optional-plesk-git-composer.md) | backlog | Plesk Git / Composer evaluieren |
|
| 10 | [10-optional-plesk-git-composer.md](10-optional-plesk-git-composer.md) | backlog | Plesk Git / Composer evaluieren |
|
||||||
| 11 | [11-boka-smtp-relay.md](11-boka-smtp-relay.md) | **medium** | boka-Apps über 0012.de-SMTP |
|
| 11 | [11-boka-smtp-relay.md](11-boka-smtp-relay.md) | **medium** | boka-Apps über 0012.de-SMTP |
|
||||||
|
| 12 | [12-monitor-php-token-check.md](12-monitor-php-token-check.md) | **medium** | HTTP-Zugriff auf `monitor.php` per Token |
|
||||||
|
|
||||||
## Backlog (ohne Priorität)
|
## Backlog (ohne Priorität)
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user