Ga naar inhoud

Beveiliging en beheer

Deze brug stuurt uw Hypertext Markup Language (HTML) over een netwerkgrens naar een browser-engine. Deze pagina documenteert de maatregelen die die grens beschermen, met de broncode als bron van waarheid. Wanneer een maatregel naar een standaard verwijst, is dat de verwijzing die in de docblock van de code is opgenomen. Deze pagina geeft de claim uit de code weer; ze reconstrueert geen normatieve formuleringen.

De docblocks van het package noemen de dreigingen waartegen het beschermt:

  • XSS-naar-PDF — Cross-site scripting (XSS) via kwaadaardige markup die tijdens het renderen naar Portable Document Format (PDF) wordt uitgevoerd.
  • SSRF — Server-side request forgery (SSRF) veroorzaakt door markup of een Uniform Resource Locator (URL) als bestemming die een verzoek naar een intern adres stuurt.
  • Uitputting van resources — Te grote invoer of een decompressiebom.
  • DNS-rebinding — Domain Name System (DNS)-rebinding, waarbij een hostnaam de validatie doorstaat en vervolgens bij het verbinden naar een privéadres verwijst.
  • On-path TLS-onderschepping — On-path Transport Layer Security (TLS)-onderschepping via een vervangen certificaat op het netwerkpad naar de Worker.

Voor elke dreiging staat hieronder een specifieke, testbare maatregel.

Invoermaatregelen (voordat het verzoek PHP verlaat)

Sectie met titel “Invoermaatregelen (voordat het verzoek PHP verlaat)”

CloudflareSecurityPolicy::validate() wordt uitgevoerd voordat er een verzoek wordt opgebouwd:

MaatregelGedragBron van de limiet
GroottelimietWeigert HTML die groter is dan maxHtmlSizeCloudflareRendererConfig, standaard 5000000 bytes
Base64-decompressiebombeveiligingSchat de gedecodeerde grootte van elke data:…;base64,…-URI; weigert waarden die het maximum bereiken of overschrijdenMAX_DATA_URI_BYTES = 13631488
Verbod op meta-refreshWeigert elke <meta http-equiv="refresh">, ongeacht hoofdlettergebruikregex in CloudflareSecurityPolicy

Een overtreding veroorzaakt een RuntimeException met een bericht dat de afwijkende waarde en de limiet noemt. Het verbod op meta-refresh bestaat omdat een refresh-directive binnen de pagina die de Worker weergeeft navigatie kan starten: een SSRF-vector in de content, niet in de URL.

Het HTML-beveiligingsbeleid uit nextpdf/core (HtmlSecurityPolicyInterface, standaard DefaultHtmlSecurityPolicy) werkt op de parse-laag en vult de bovenstaande transportlaagcontroles aan. U haalt het op met getHtmlSecurityPolicy(). Injecteer via de constructor een eigen variant.

Bestemmingsmaatregelen (SSRF en DNS-rebinding)

Sectie met titel “Bestemmingsmaatregelen (SSRF en DNS-rebinding)”

CloudflareSecurityPolicy::validateWorkerUrl():

  1. Weigert een URL die niet kan worden geparseerd of geen scheme/host bevat (Invalid Worker URL).
  2. Weigert elk ander scheme dan HTTPS (Worker URL must use HTTPS).
  3. Voor een host die een IP-literal is, weigert private of gereserveerde bereiken met PHP’s FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE. In de praktijk weigert dit de private adresruimte van RFC 1918, loopback en de link-local-adressen van RFC 3927. Tests dekken expliciet dat 192.168.x, 127.0.0.1 en 169.254.x worden geweigerd. De PHP-filterextensie bepaalt of een adres tot een bereik behoort; dit package koppelt die beslissing niet aan een clausule. RFC 1918 en RFC 3927 worden hier beschrijvend genoemd als de bekende definities van die bereiken.
  4. Voor een hostnaam resolvet de methode alle A- en AAAA-records met dns_get_record() (niet gethostbyname(), dat alleen het eerste antwoord teruggeeft) en weigert de host als enig geresolved adres privé of gereserveerd is.

Het resolven van alle records is opzettelijk. De docblock van de klasse beschrijft dit als verdediging tegen een host die meerdere records teruggeeft, waarbij een lookup van één record het openbare adres zou kunnen kiezen terwijl de latere verbinding een privéadres kiest. Dit komt overeen met de OWASP SSRF Prevention Cheat Sheet: resolve de A- en AAAA-antwoorden voor het domein en pas de controle op niet-openbare adressen toe op de volledige resultatenset.

validateWorkerUrl() geeft de gecontroleerde IP-set terug. Vlak voor verzending roept de renderer assertPinsStillValid() aan. Die aanroep resolvet de host opnieuw en weigert elk nieuw gezien IP (Worker URL DNS answer changed since validation — possible DNS rebinding attack). Dit sluit het time-of-check/time-of-use-venster tussen validatie en verbinding.

Wanneer een gecontroleerde IP-set of een Subject Public Key Info (SPKI)-pinset aanwezig is en er een PHP Standards Recommendation 17 (PSR-17) ResponseFactory is meegegeven, gebruikt de renderer Transport\PinnedCurlTransport in plaats van de geïnjecteerde PHP Standards Recommendation 18 (PSR-18)-client. Het transport handhaaft deze maatregelen op cURL-handle-niveau:

  • Gepinde DNSCURLOPT_RESOLVE bindt de host:poort aan de gecontroleerde IP-set, zodat libcurl bij het verbinden geen eigen lookup uitvoert. Door deze binding geldt de userland-DNS-controle voor de daadwerkelijke verbinding; zonder die binding zou libcurl een ander adres kunnen resolven.
  • TLS-public-key-pinningCURLOPT_PINNEDPUBLICKEY wordt ingesteld op basis van de gecombineerde pinset. Dit volgt RFC 7469 §2.6: een gepinde verbinding wordt geaccepteerd wanneer de door de server gepresenteerde SPKI-fingerprintset overlapt met de geconfigureerde pinset, en een mislukte pinvalidatie is niet herstelbaar. Pin-strings worden genormaliseerd van sha256/<base64> naar de sha256//<base64>-vorm van cURL; een ongeldige pin veroorzaakt een InvalidSpkiPinException.
  • TLS-verificatie aanCURLOPT_SSL_VERIFYPEER => true, CURLOPT_SSL_VERIFYHOST => 2.
  • Geen automatische redirectsCURLOPT_FOLLOWLOCATION => false, CURLOPT_MAXREDIRS => 0. Een 3xx-respons wordt aan de beleidslaag doorgegeven in plaats van door libcurl naar een niet-gecontroleerde host te worden gevolgd. De docblock van de klasse stelt dat dit opzettelijk is, zodat redirects opnieuw worden gevalideerd in plaats van stilzwijgend te worden gevolgd.
  • Harde time-outCURLOPT_TIMEOUT wordt ingesteld op basis van renderTimeout (standaard 30 seconden).

Een cURL-fout of een body die geen string is, veroorzaakt een CloudflareRenderException met het cURL-foutnummer en -bericht.

De configuratie bevat pinnedPublicKeys en een aparte backupPublicKeys. RFC 7469 §2.5 beschrijft een back-uppin als een fingerprint voor een secundair, nog niet uitgerold sleutelpaar dat offline wordt bewaard, en behandelt deze als de primaire herstelweg bij een onbedoeld mislukte pinvalidatie. Houd ten minste één back-uppin aan, zodat certificaatrotatie het endpoint niet onbruikbaar maakt. Via het aparte veld kunt u een rotatie onafhankelijk valideren. Operationeel:

  • Pin de SPKI van het leaf-certificaat of van een intermediate waarvan u de rotatie beheert.
  • Configureer altijd een back-uppin voor het volgende certificaat voordat u roteert.
  • Een lege pinset schakelt pinning uit; gebruik dat alleen bij een stabiele, bekende certificaatketen. Pinning is opt-in via configuratie.
  • Het Worker-verzoek bevat Authorization: Bearer <apiToken>. apiToken is #[SensitiveParameter], zodat stack traces deze maskeren. De bereikbaarheidsprobe verstuurt dezelfde bearer-header bij een Hypertext Transfer Protocol (HTTP) HEAD.
  • Cloudflare R2-toegangssleutels (accessKeyId, secretAccessKey) zijn #[SensitiveParameter] en worden alleen gebruikt om de Amazon Web Services (AWS) Signature V4-ondertekensleutel af te leiden.
  • ApiKeyValidator vergelijkt sleutels met hash_equals() (timing-veilig) en ondersteunt opslag van Secure Hash Algorithm 256 (SHA-256)-gehashte sleutels via validateHashed().
  • Configuratieobjecten zijn final readonly — een eenmaal ingesteld geheim kan niet worden gewijzigd.
  • Haal geheimen uit omgevingsvariabelen of een secrets manager. Commit ze nooit. Het package volgt de bredere beveiligingsbasislijn van NextPDF: PHPStan Level 10, declare(strict_types=1) in elk bestand, geen eval()/exec(), GitHub Actions vastgepind op SHA.
  • Het noemt geen enkele Cloudflare-platformlimiet (CPU-tijd van de Worker, geheugen, maximale verzoekbody of aantal subverzoeken). De enige grootte- en tijdslimieten die deze documentatie noemt, zijn de limieten die het package zelf afdwingt, hierboven vermeld en in /integrations/cloudflare/configuration/. Raadpleeg voor platformlimieten de officiële documentatie van Cloudflare en de eigen implementatie van uw Worker.
  • Het ondertekent geen PDF’s en doet geen enkele bewering over conformiteit van handtekeningen. Wanneer handtekeningen vereist zijn, render dan hier en onderteken daarna met de engine. NextPDF Pro biedt alleen PDF Advanced Electronic Signatures (PAdES) B-B-ondertekening; profielen voor langetermijnvalidatie zijn een Enterprise-functie en vallen buiten de scope van deze brug.
  • Het certificeert of garandeert de pijplijn niet en maakt deze niet „tamper-proof”. Het implementeert uitsluitend de specifieke, in de broncode verifieerbare maatregelen die op deze pagina worden beschreven.
SymptoomEerste controle
Worker URL must use HTTPSControleer het scheme van de geconfigureerde workerUrl.
private or reserved IPControleer de DNS-records van de Worker-hostnaam; zoek naar een record dat resolvet naar de adresruimte van RFC 1918 / loopback / RFC 3927.
DNS answer changed since validationDNS-instabiliteit of een rebindingpoging; resolve opnieuw en inspecteer de volledige recordset.
cURL transport errorControleer het netwerkpad, de TLS-keten en — als er pins zijn ingesteld — of de SPKI van het aangeboden certificaat nog in de pinset staat.
Renderings mislukken direct na een certificaatrotatieEen pinset zonder bijbehorende back-uppin. Voeg de nieuwe SPKI als back-up toe voordat u roteert.
is not installed / no LocalRendererFactoryInterfaceFallback is ingeschakeld maar er is geen factory aangesloten, of nextpdf/artisan ontbreekt.
Weigeringen door de rate limit zijn inconsistent tussen nodesDe in-memory limiter werkt per proces; plaats er een gedeelde store voor.

Meld kwetsbaarheden via GitHub Security Advisories of het beveiligingscontact in de SECURITY.md van de repository. Dien beveiligingsproblemen niet in als openbare GitHub-issues.

  • /integrations/cloudflare/overview/ — waarom dit package rond deze grens is opgebouwd.
  • /integrations/cloudflare/configuration/ — velden voor pinset en limieten.
  • /integrations/cloudflare/troubleshooting/ — volledige toewijzing van fouten naar exceptions.