Sicherheit und Betrieb
Auf einen Blick
Abschnitt betitelt „Auf einen Blick“Diese Brücke sendet Ihr HTML über eine Netzwerkgrenze hinweg an eine Browser-Engine. Diese Seite dokumentiert anhand des Quellcodes jede Schutzmaßnahme, die diese Grenze absichert. Wenn eine Schutzmaßnahme einen Standard zitiert, entspricht das Zitat den Angaben im DocBlock des Codes. Diese Seite gibt die Aussage des Codes wieder und rekonstruiert keinen normativen Wortlaut.
Bedrohungsmodell
Abschnitt betitelt „Bedrohungsmodell“Die DocBlocks des Pakets selbst benennen die Bedrohungen, gegen die es schützt:
- XSS-zu-PDF — feindliches Markup, das während des Renderings ausgeführt wird.
- SSRF — Markup oder eine Ziel-URL, die eine Anfrage an eine interne Adresse auslöst.
- Ressourcenerschöpfung — übergroße Eingabe oder eine Dekomprimierungsbombe.
- DNS-Rebinding — ein Hostname, der die Validierung besteht und sich dann zum Verbindungszeitpunkt zu einer privaten Adresse auflöst.
- On-Path-TLS-Interception — ein ausgetauschtes Zertifikat auf dem Pfad zum Worker.
Jede dieser Bedrohungen wird weiter unten durch eine spezifische, testbare Schutzmaßnahme adressiert.
Eingabekontrollen (bevor die Anfrage PHP verlässt)
Abschnitt betitelt „Eingabekontrollen (bevor die Anfrage PHP verlässt)“CloudflareSecurityPolicy::validate() wird ausgeführt, bevor eine Anfrage konstruiert wird:
| Schutzmaßnahme | Verhalten | Quelle des Limits |
|---|---|---|
| Größenobergrenze | Weist HTML zurück, das größer ist als maxHtmlSize | CloudflareRendererConfig, Standard 5000000 Bytes |
| Base64-Dekomprimierungsbomben-Schutz | Schätzt die dekodierte Größe jeder data:…;base64,…-URI; weist sie bei Erreichen oder Überschreiten der Obergrenze zurück | MAX_DATA_URI_BYTES = 13631488 |
| Meta-Refresh-Verbot | Weist jedes <meta http-equiv="refresh"> zurück, ohne Berücksichtigung der Groß-/Kleinschreibung | Regex in CloudflareSecurityPolicy |
Ein Verstoß löst eine RuntimeException mit einer Meldung aus, die den beanstandeten Wert und das Limit nennt. Das Meta-Refresh-Verbot besteht, weil eine Refresh-Direktive innerhalb der vom Worker gerenderten Seite eine Navigation auslösen kann — ein SSRF-Vektor, der im Inhalt steckt, nicht in der URL.
Die HTML-Sicherheitsrichtlinie aus nextpdf/core (HtmlSecurityPolicyInterface, Standard DefaultHtmlSecurityPolicy) arbeitet auf der Parsing-Ebene und ergänzt die oben genannten Prüfungen auf Transportebene. Rufen Sie sie mit getHtmlSecurityPolicy() ab. Übergeben Sie eine benutzerdefinierte Richtlinie über den Konstruktor.
Zielkontrollen (SSRF und DNS-Rebinding)
Abschnitt betitelt „Zielkontrollen (SSRF und DNS-Rebinding)“CloudflareSecurityPolicy::validateWorkerUrl():
- Weist eine URL zurück, die sich nicht parsen lässt oder bei der scheme/host fehlt (
Invalid Worker URL). - Weist jedes Nicht-HTTPS-Schema zurück (
Worker URL must use HTTPS). - Bei einem IP-Literal-Host weist sie private oder reservierte Bereiche mithilfe von
PHPs
FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE. In der Praxis weist dies den privaten Bereich nach RFC 1918, Loopback- und Link-Local-Adressen nach RFC 3927 zurück — die Tests prüfen die Zurückweisungen von192.168.x,127.0.0.1und169.254.xausdrücklich. Die Zugehörigkeit zu einem Bereich wird von der Filter-Erweiterung von PHP entschieden, nicht anhand einer Klausel, auf die sich dieses Paket festlegt; RFC 1918 und RFC 3927 werden hier beschreibend als die bekannten Definitionen dieser Bereiche genannt. - Bei einem Hostnamen löst sie alle A- und AAAA-Records über
dns_get_record()auf (nichtgethostbyname(), das nur die erste Antwort zurückgibt) und weist zurück, wenn auch nur eine aufgelöste Adresse privat oder reserviert ist.
Dass sämtliche Records aufgelöst werden, ist beabsichtigt und im DocBlock der Klasse als Abwehr gegen einen Host dokumentiert, der mehrere Records zurückgibt: Eine Einzel-Record-Abfrage könnte einen öffentlichen Record auswählen, während die spätere Verbindung einen privaten wählt. Dies entspricht dem OWASP SSRF Prevention Cheat Sheet, das einer Anwendung vorgibt, alle IP-Adressen hinter dem Domainnamen abzurufen (A- und AAAA-Records) und die Prüfung auf nicht öffentliche Adressen auf jede einzelne anzuwenden.
validateWorkerUrl() gibt die geprüfte IP-Menge zurück. Der Renderer ruft dann unmittelbar vor dem Senden der Anfrage assertPinsStillValid() auf. Dieser Aufruf löst den Host erneut auf und weist zurück, wenn seit der Validierung eine neue IP aufgetaucht ist (Worker URL DNS answer changed since validation — possible DNS rebinding attack). Damit wird das Time-of-Check-/Time-of-Use-Fenster zwischen Validierung und Verbindung geschlossen.
Transportkontrollen (PinnedCurlTransport)
Abschnitt betitelt „Transportkontrollen (PinnedCurlTransport)“Wenn eine geprüfte IP-Menge oder eine SPKI-Pin-Menge vorhanden ist und eine PSR-17-ResponseFactory bereitgestellt wurde, verwendet der Renderer Transport\PinnedCurlTransport anstelle des injizierten PSR-18-Clients. Der Transport erzwingt auf Ebene des cURL-Handles:
- Gepinntes DNS —
CURLOPT_RESOLVEbindet host:port an die geprüfte IP-Menge, sodass libcurl zum Verbindungszeitpunkt keine eigene Auflösung durchführt. Dadurch wird sichergestellt, dass die Userland-DNS-Prüfung die Verbindung tatsächlich bindet; ohne diese Bindung könnte libcurl eine andere Adresse auflösen. - TLS-Public-Key-Pinning —
CURLOPT_PINNEDPUBLICKEYwird aus der kombinierten Pin-Menge gesetzt. Dies folgt RFC 7469 §2.6: Eine gepinnte Verbindung wird akzeptiert, wenn sich die Menge der vom Server vorgelegten SPKI-Fingerabdrücke mit der konfigurierten Pin-Menge überschneidet, und ein Fehlschlag der Pin-Validierung wird als nicht behebbar behandelt. Pin-Strings werden vonsha256/<base64>in diesha256//<base64>-Form von cURL normalisiert; ein fehlerhafter Pin löst eineInvalidSpkiPinExceptionaus. - TLS-Verifizierung aktiviert —
CURLOPT_SSL_VERIFYPEER => true,CURLOPT_SSL_VERIFYHOST => 2. - Keine automatischen Redirects —
CURLOPT_FOLLOWLOCATION => false,CURLOPT_MAXREDIRS => 0. Ein 3xx wird an die Richtlinienebene weitergereicht, statt dass libcurl ihm zu einem ungeprüften Host folgt. Der DocBlock der Klasse hält fest, dass dies eine bewusste Entscheidung ist, damit Redirects erneut validiert und nicht stillschweigend befolgt werden. - Hartes Timeout —
CURLOPT_TIMEOUTwird ausrenderTimeoutgesetzt (Standard30Sekunden).
Ein cURL-Fehler oder ein Body, der kein String ist, löst eine CloudflareRenderException mit cURL-Fehlernummer und -meldung aus.
Betriebliche Hinweise zum Pinning
Abschnitt betitelt „Betriebliche Hinweise zum Pinning“Die Konfiguration enthält pinnedPublicKeys und ein separates backupPublicKeys. RFC 7469 §2.5 beschreibt einen Backup-Pin — einen Fingerabdruck für ein sekundäres, noch nicht bereitgestelltes Schlüsselpaar, das offline gehalten wird — als primären Weg, sich von einem versehentlichen Fehlschlag der Pin-Validierung zu erholen. Es entspricht dieser Empfehlung, mindestens einen Backup-Pin vorzuhalten, damit eine Zertifikatsrotation den Endpunkt nicht unbrauchbar macht. Das separate Feld erlaubt es, eine Rotation unabhängig zu validieren. Im Betrieb:
- Pinnen Sie das SPKI des Leaf-Zertifikats oder eines Zwischenzertifikats, dessen Rotation Sie kontrollieren.
- Konfigurieren Sie vor der Rotation stets einen Backup-Pin für das nächste Zertifikat.
- Eine leere Pin-Menge deaktiviert das Pinning; verwenden Sie das nur bei einer stabilen, bekannten Zertifikatskette. Pinning ist per Konfiguration aktivierbar (Opt-in).
Authentifizierung und Geheimnisverwaltung
Abschnitt betitelt „Authentifizierung und Geheimnisverwaltung“- Die Worker-Anfrage enthält
Authorization: Bearer <apiToken>.apiTokenist#[SensitiveParameter], sodass er aus Stack-Traces entfernt wird. Die Erreichbarkeitsprüfung sendet denselben Bearer-Header über ein HTTP-HEAD. - R2-Zugriffsschlüssel (
accessKeyId,secretAccessKey) sind#[SensitiveParameter]und werden ausschließlich verwendet, um den Signierschlüssel für AWS Signature V4 abzuleiten. ApiKeyValidatorvergleicht Schlüssel mithash_equals()(timing-sicher) und unterstützt die Speicherung gehashter Schlüssel mit SHA-256 übervalidateHashed().- Konfigurationsobjekte sind
final readonly— ein einmal gesetztes Geheimnis kann nicht verändert werden. - Beziehen Sie Geheimnisse aus Umgebungsvariablen oder einem Secrets-Manager. Committen Sie sie niemals. Das Paket folgt der umfassenderen Sicherheitsbasis von NextPDF: PHPStan Level 10,
declare(strict_types=1)in jeder Datei, keineval()/exec(), GitHub Actions sind auf SHA festgelegt.
Was dieses Paket nicht behauptet
Abschnitt betitelt „Was dieses Paket nicht behauptet“- Es nennt kein Cloudflare-Plattformlimit (Worker-CPU-Zeit, Speicher, Obergrenze des Anfrage-Bodys oder Anzahl der Subrequests). Die einzigen Größen- und Zeitlimits, die diese Dokumentation nennt, sind die, die das Paket selbst erzwingt; sie sind oben und unter /integrations/cloudflare/configuration/ aufgeführt. Konsultieren Sie für Plattformlimits die offizielle Dokumentation von Cloudflare und die Implementierung Ihres eigenen Workers.
- Es signiert keine PDFs und erhebt keinen Anspruch auf Signatur-Konformität. Wenn Signaturen erforderlich sind, rendern Sie hier und signieren Sie anschließend mit der Engine. NextPDF Pro bietet ausschließlich PAdES B-B-Signierung; Profile für die Langzeitvalidierung sind eine Enterprise-Funktion und liegen außerhalb des Geltungsbereichs dieser Brücke.
- Es zertifiziert oder garantiert nichts und macht die Pipeline auch nicht „manipulationssicher“. Es implementiert die spezifischen, anhand des Quellcodes überprüfbaren Schutzmaßnahmen, die auf dieser Seite beschrieben sind, und nichts darüber hinaus.
Betriebliches Runbook
Abschnitt betitelt „Betriebliches Runbook“| Symptom | Erste Prüfung |
|---|---|
Worker URL must use HTTPS | Das Schema der konfigurierten workerUrl. |
private or reserved IP | Die DNS-Records des Worker-Hostnamens; ein Record, der sich in einen Bereich nach RFC 1918 / Loopback / RFC 3927 auflöst. |
DNS answer changed since validation | DNS-Instabilität oder ein Rebinding-Versuch; lösen Sie erneut auf und prüfen Sie die Record-Menge. |
cURL transport error | Netzwerkpfad, TLS-Kette und — falls Pins gesetzt sind — ob das SPKI des ausgelieferten Zertifikats noch in der Pin-Menge enthalten ist. |
| Renderings schlagen direkt nach einer Zertifikatsrotation fehl | Eine Pin-Menge ohne passenden Backup-Pin. Fügen Sie das neue SPKI vor dem Rotieren als Backup hinzu. |
is not installed / no LocalRendererFactoryInterface | Fallback aktiviert, aber keine Factory verdrahtet, oder nextpdf/artisan nicht vorhanden. |
| Ratenbegrenzungs-Ablehnungen über Knoten hinweg inkonsistent | Der In-Memory-Limiter arbeitet pro Prozess; schalten Sie einen gemeinsam genutzten Store vor. |
Vorfallmeldung
Abschnitt betitelt „Vorfallmeldung“Melden Sie Schwachstellen über GitHub Security Advisories oder den Sicherheitskontakt in der SECURITY.md des Repositorys. Reichen Sie Sicherheitsprobleme nicht als öffentliche GitHub-Issues ein.
Siehe auch
Abschnitt betitelt „Siehe auch“- /integrations/cloudflare/overview/ — warum das Paket um diese Grenze herum konzipiert ist.
- /integrations/cloudflare/configuration/ — Felder für Pin-Mengen und Limits.
- /integrations/cloudflare/troubleshooting/ — vollständige Zuordnung von Fehlern zu Exceptions.