blockExternalSrcs() filterte bisher nur src= bei img/video/audio/source und ließ Tracking-Pixel via <link href>, background=, CSS url() (Style-Block und inline) sowie srcset durch — DSGVO-relevantes Read-Tracking trotz aktivierter Blockierung. Zusätzlich deckt die neue Erkennung protokollrelative URLs (//host/px.gif) ab, die die alte https?:-Prüfung durchließ. data:/cid:-URIs bleiben unangetastet, <a href> weiterhin klickbar. CSP-Header als robustere Ergänzung (Blocklist-Regex bleibt grundsätzlich umgehbar) folgt separat mit dem firewall-security-Skill. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019j28kGcaJAhBnrYX34hGdt
3.4 KiB
id, title, status, created
| id | title | status | created |
|---|---|---|---|
| PROJ-76 | Mail-HTML-Sanitizing schließt CSS-url()/link/srcset nicht ein (Tracking-Pixel-Umgehung) | In Review | 2026-08-05 |
Problem
blockExternalSrcs() (src/app/mail/[id]/page.tsx, Zeile ~76-83) filtert
per Regex ausschließlich src=-Attribute in img|video|audio|source-Tags.
Nicht abgedeckt:
<link rel="stylesheet" href=...>background=-Attribut- CSS
url()in<style>-Blöcken oderstyle=-Attributen srcset=
Solche Remote-Referenzen laden auch im aktuellen "blockiert"-Zustand und ermöglichen Read-Tracking (Öffnungs-Tracking-Pixel) trotz aktivierter Blockierung — DSGVO-relevant, da archivierte E-Mails von Nutzern als "sicher betrachtet" gelesen werden.
Regex-basiertes HTML-Filtern ist grundsätzlich umgehbar; eine robuste Lösung braucht serverseitiges Sanitizing oder einen CSP-Header statt reiner Client-Regex.
Lösung (Vorschlag)
- Kurzfristig: Regex in
blockExternalSrcs()umlink[href],background=,srcset=und CSS-url()-Vorkommen erweitern. - Mittelfristig: serverseitiges HTML-Sanitizing beim Rendern der Mail (z.B. Allowlist-basierter Sanitizer statt Blocklist-Regex) statt ausschließlich clientseitigem Regex-Blocking.
- Alternative/Ergänzung: CSP-Header (
Content-Security-Policymitimg-src 'none'etc.) auf der Mail-Detail-Route, damit der Browser selbst externe Loads blockiert statt sich auf Regex-Vorverarbeitung zu verlassen.
Sollte mit dem firewall-security-Skill abgestimmt werden (CSP-Header-
Konfiguration liegt in dessen Zuständigkeit).
Implementation Notes
Umgesetzt: Punkt 1 der Lösung (Regex-Erweiterung). Punkt 2 (serverseitiges Sanitizing) und Punkt 3 (CSP-Header) bleiben offen.
src/app/mail/[id]/page.tsx:80-165 — blockExternalSrcs() komplett ersetzt.
Statt zwei Einzel-Regexes auf src= jetzt ein zweistufiger Durchlauf:
<style>…</style>-Blöcke: externeurl(...)→url(about:blank)(blockCssUrls()).- Tag-Walker über alle Tags;
blockTagAttributes()prüftsrc,srcset,href,background,style(quoted/unquoted Werte):src→data-srcnur beiimg|video|audio|source(wie bisher)srcset→data-srcsetbeiimg|source, wenn ein kommagetrennter Kandidat extern isthref→data-hrefnur bei<link>(<a href>bleibt klickbar)background→data-background(Legacy-HTML-Mails)style→ externeurl(...)neutralisiert
Externerkennung via EXTERNAL_URL_RE = /^\s*(?:https?:)?\/\//i — deckt
zusätzlich protokollrelative URLs (//host/px.gif) ab, die die alte
["']https?:-Prüfung durchgelassen hätte. data:- und cid:-URIs werden
nicht angefasst, Inline-Bilder rendern weiter.
Manuell gegengeprüft (Node-Snippet, 14 Fälle): alle genannten Tracking-Vektoren
werden neutralisiert, data:/cid:/<a href>/Plaintext bleiben unverändert.
npx tsc --noEmit fehlerfrei.
Bekannte Restlücken (Blocklist-Ansatz bleibt umgehbar, daher Punkt 2/3):
@import "http://…" in CSS, <iframe|embed>-Quellen, Meta-Refresh.
Acceptance Criteria
- Test-Mail mit Tracking-Pixel via
<link>,background=, CSSurl()undsrcsetlädt keine externe Ressource mehr in der Mail-Detailansicht. - Bestehende, bereits blockierte Vektoren (
img|video|audio|sourcesrc=) weiterhin blockiert (Regressionstest). - CSP-Header-Ansatz mit firewall-security-Skill abgestimmt.