Ga naar inhoud

NextPDF Gotenberg-beveiliging en -beheer

Deze bridge verstuurt documenten vanuit uw applicatie via het netwerk naar een externe service. Daarmee vormt hij zowel een server-side-request-oppervlak als een oppervlak voor transportbeveiliging. Het pakket implementeert voor beide specifieke, verifieerbare controles. Op zichzelf beveiligt het niet het volledige systeem. Die controles werken alleen wanneer u Gotenberg uitrolt en beheert met de bijbehorende beveiligingsmaatregelen. Deze pagina legt uit welke controles het pakket implementeert en welke operationele taken die controles compleet maken.

Niets hiervan is een garantie. Elke controle is gedefinieerd, door tests gedekt gedrag met expliciete grenzen.

De bridge past twee verschillende beveiligingsbeleidsregels toe op verschillende lagen:

  • Transportbeleid (GotenbergSecurityPolicy) — afdwingen van het URL-schema, screening op server-side request forgery (SSRF), verdediging tegen Domain Name System (DNS) rebinding, limieten voor invoergrootte en screening van bestandsnamen. Dit is de laag die hieronder in detail wordt beschreven.
  • HTML-parsebeleid — een inhoudsbeleid op de parselaag, dat standaard terugvalt op het standaardbeleid van de NextPDF-kern en wordt uitgevoerd voordat de inhoud een renderer bereikt. Het vormt een aanvulling op het transportbeleid, maar blijft daar onafhankelijk van. Deze pagina behandelt het transportbeleid.

Screening op server-side request forgery (SSRF)

Sectie met titel “Screening op server-side request forgery (SSRF)”

De bridge screent de geconfigureerde API-URL voordat er enige byte het proces verlaat. De controle bestaat uit drie onderdelen.

Afdwingen van het schema. Alleen https wordt geaccepteerd (hoofdletterongevoelig). Een gewone http://-URL wordt afgewezen. Transport Layer Security (TLS) is daarom verplicht voor elke conversie en voor de health-probe.

Adresscreening. Als de host een IP-literal is, wijst de bridge die af wanneer het adres binnen een privé- of gereserveerd bereik valt. Als de host een naam is, resolvt de bridge die naar alle A- en AAAA-records en wijst hij het verzoek af als enig geresolved adres privé of gereserveerd is. Het resolven van de volledige recordset, in plaats van één enkel adres, is de controle die een aanvaller tegenhoudt die een privéadres verbergt achter een naam die ook een publiek adres teruggeeft. Dit volgt de aanpak van de OWASP-richtlijn voor SSRF-preventie: haal elk IP-adres achter de domeinnaam op (A- en AAAA-records, voor IPv4 en IPv6) en valideer elk daarvan tegen een allowlist (OWASP Cheat Sheet Series, SSRF prevention, application-layer defense; vastgelegd in de retrieval-augmented generation (RAG)-sidecar van de pagina).

Hercontrole tegen time-of-check/time-of-use (TOCTOU). Vlak voor het verzoek resolvt de bridge opnieuw en vergelijkt hij de adresset die tijdens de validatie is vastgelegd. Als er een nieuw adres verschijnt, wordt het verzoek afgebroken met een DNS-rebinding-fout. Dit sluit het tijdvenster tussen validatie en verbinding dat een rebinding-aanval anders zou misbruiken.

Wanneer de bridge zijn cURL-gepinde transport gebruikt, wordt de gevalideerde adresset met CURLOPT_RESOLVE aan de verbinding gebonden. Daardoor maakt de kernel verbinding met het gecontroleerde adres, in plaats van met wat een nieuwe DNS-lookup op het moment van verbinden zou kunnen teruggeven. Het volgen van redirects is op dat transport uitgeschakeld (CURLOPT_FOLLOWLOCATION uit, CURLOPT_MAXREDIRS nul), zodat een 3xx het verzoek niet ongemerkt naar een ongecontroleerde host kan sturen. De beleidslaag ontvangt in plaats daarvan de respons.

Operationeel gevolg. De SSRF-bewaking wijst privé- en gereserveerde adressen standaard af. Als uw Gotenberg op een privénetwerk draait, kunt u de bridge niet naar het privéadres ervan laten wijzen. Maak de service beschikbaar via een adres dat de bewaking accepteert en bescherm dat pad met netwerksegmentatie en authenticatie, zoals hieronder onder deployment beschreven.

Transportbeveiliging en pinning van de publieke sleutel

Sectie met titel “Transportbeveiliging en pinning van de publieke sleutel”

TLS-peer- en hostverificatie staan altijd aan in het cURL-gepinde transport (CURLOPT_SSL_VERIFYPEER true, CURLOPT_SSL_VERIFYHOST 2). Bovenop de standaard ketenvalidatie ondersteunt de bridge SubjectPublicKeyInfo (SPKI)-pinning.

Elke pin is een SHA-256-hash van de SubjectPublicKeyInfo van de server, uitgedrukt als sha256/<base64>. De bridge accepteert een certificaat wanneer de SPKI-hash ervan overeenkomt met een pin in de gecombineerde set van primaire en back-uppins. Dit model met back-uppins volgt RFC 7469 §4.3, dat een back-uppin — een fingerprint van een secundair, nog niet uitgerold sleutelpaar — aanwijst als het primaire herstelpad bij een onbedoelde mislukte pin-validatie, en §2.5, dat vereist dat de gepinde set ten minste één pin bevat die niet aanwezig is in de huidige certificaatketen (RFC 7469 §4.3 en §2.5; vastgelegd in de RAG-sidecar van de pagina). De code van de bridge declareert RFC 7469 §2.1 en §2.6 voor de semantiek van ten-minste-één-back-uppin en de intersectie van de gecombineerde set. Pinning is opt-in: zonder geconfigureerde pins geldt de standaard ketenvalidatie en wordt pinning niet afgedwongen.

Een pin die niet parseerbaar is, veroorzaakt vóór enig verzoek een configuratiefout. Een live certificaat waarvan de SPKI met geen enkele geconfigureerde pin overeenkomt, zorgt ervoor dat het transport het verzoek standaard laat mislukken.

Een onjuiste rotatie sluit de bridge buiten van de service. Roteer zonder storing:

  1. Genereer voordat u de serversleutel wijzigt de SPKI-pin voor de nieuwe sleutel en voeg deze toe aan de lijst met back-uppins. Rol die configuratie uit. De bridge accepteert nu zowel de huidige als de toekomstige sleutel.
  2. Wissel het servercertificaat of de serversleutel om naar de nieuwe sleutel.
  3. Bevestig dat conversies nog steeds slagen (de nieuwe sleutel komt nu overeen met de back-uppin).
  4. Verplaats de nieuwe pin van de back-uplijst naar de primaire lijst en verwijder de pin van de buiten gebruik gestelde sleutel. Rol uit.
  5. Genereer en bereid de pin voor de volgende rotatie voor als de nieuwe back-up, zodat de set altijd een bruikbare reserve bevat.

Door de back-uplijst gescheiden te houden van de primaire lijst kunt u de volgende pin voorbereiden en valideren zonder de actieve pin te wijzigen.

Wanneer apiKey niet leeg is, verstuurt de bridge deze als een Authorization: Bearer-header op het conversieverzoek. Het veld is gemarkeerd met #[\SensitiveParameter], zodat de waarde wordt weggelaten uit stacktraces. De bridge laadt het geheim niet voor u; lever het bij het starten van het proces aan vanuit een secret manager en commit het nooit. Het token wordt niet naar de request-log geschreven — de gelogde debug-vermelding bevat alleen de URL, de bestandsnaam, het formaat en de contentlengte.

Een respons wordt alleen geaccepteerd wanneer de status 200 is, de Content-Type application/pdf bevat en de body begint met de %PDF-signatuur. De byte-signatuurcontrole is van belang omdat een opgegeven contenttype op zichzelf niet bewijst wat de bytes zijn. De WHATWG MIME Sniffing-standaard formaliseert dezelfde redenering in zijn MIME-type-sniffingalgoritme, dat een berekend type afleidt uit een match met het beginnende bytepatroon in plaats van uit het aangeleverde type. De OWASP-richtlijn voor bestandsuploads benoemt het bijbehorende applicatieprincipe: valideer het bestandstype en vertrouw de opgegeven Content-Type-header niet, omdat die vervalst kan worden (WHATWG MIME Sniffing §6.2.3; OWASP Cheat Sheet Series, file-upload validation; beide vastgelegd in de RAG-sidecar van de pagina). De bridge past defensief de gelijkwaardige controle toe aan de inkomende zijde: een verschil veroorzaakt een getypte exception en de bytes worden nooit als resultaat teruggegeven.

Deze grens is ook de reden waarom het PSR-18-contract hier van belang is. Een PSR-18-client gooit alleen een exception wanneer hij het verzoek niet kan verzenden of de respons niet in een PSR-7-object kan parseren — hij gooit geen exception bij een foutstatuscode. Een welgevormde 4xx/5xx-respons wordt normaal aan de aanroeper teruggegeven (PSR-18, “Exceptions”; vastgelegd in de RAG-sidecar van de pagina). De bridge inspecteert daarom zelf de status, het type en de signatuur in plaats van aan te nemen dat een teruggegeven respons succesvol is. De HTTP-semantiek voor een schending van een content-type-beperking — een 415 (Unsupported Media Type)-afwijzing, waarbij een server inhoud in een niet-ondersteund formaat weigert — vormt het model dat de inkomende controle nabootst (RFC 9110 §15.5.16; vastgelegd in de RAG-sidecar van de pagina).

De bridge heeft één in-process resourcegrens: maxFileSize (standaard 52.428.800 bytes = 50 MiB). Deze wordt vóór het verzoek afgedwongen, zodat te grote invoer de service nooit bereikt. De bridge heeft geen ingebouwde gelijktijdigheidslimiet, rate limit of bovengrens voor de uitvoergrootte. Dat zijn verantwoordelijkheden van de deployment en de aanroeper (zie /integrations/gotenberg/production-usage/). Stel maxFileSize in op de kleinste waarde die uw echte documenten vereisen — een strakkere bovengrens is een goedkopere denial-of-service-controle.

De bridge is alleen zo veilig als de service die hij aanroept. U beheert die service; de onderstaande taken maken de bovenstaande controles compleet.

  • Beëindig TLS vóór Gotenberg. De container van Gotenberg spreekt standaard gewoon HTTP. De bridge vereist HTTPS, dus plaats Gotenberg achter een TLS-beëindigende reverse proxy, ingress of service mesh en laat de bridge naar het HTTPS-endpoint wijzen. Pin de SPKI van de proxy als u pinning inschakelt.
  • Stel Gotenberg niet publiek beschikbaar. Gotenberg voert documentconversie uit met LibreOffice- en Chromium-achtige engines en is geen internetgerichte service. Beperk de inkomende toegang tot de applicatiehosts die de service aanroepen, met netwerkbeleid of firewallregels.
  • Vereis authenticatie op het pad. De bridge verstuurt een bearer-token wanneer dit is geconfigureerd; dwing dit (of mutual TLS) af bij de proxy, zodat een niet-geauthenticeerd verzoek de conversie-engine niet kan bereiken.
  • Pin een specifieke serviceversie. De bridge gaat uit van precies twee servicepaden — /forms/libreoffice/convert en /health. Pin de Gotenberg-image op een specifieke patch-tag, verifieer die twee paden tegen de versie die u uitrolt en verifieer ze opnieuw bij elke upgrade.
  • Dimensioneer de conversiecapaciteit bewust. Elke conversie houdt gedurende het verzoek een worker bezig. Dimensioneer de Gotenberg-deployment op uw piek van gelijktijdige conversies en begrens de lopende conversies aan de aanroepzijde overeenkomstig. Capaciteit is een eigenschap van uw deployment, niet van dit pakket.
  • Behandel conversie-invoer als onbetrouwbaar. Documenten die door conversie worden gehaald, worden verwerkt door complexe engines. Beperk maxFileSize, isoleer de Gotenberg-deployment (een eigen netwerksegment, minimale egress, geen toegang tot interne services) en houd de engine gepatcht.
  • Het is niet „standaard veilig”: de controles zijn echt, maar ze zijn afhankelijk van een correcte deployment en configuratie.
  • Het maakt conversie niet „sabotagebestendig” en de uitvoer niet „gecertificeerd”. Het valideert het transport en de vorm van de respons; het attesteert de documentinhoud niet.
  • Het levert geen ondertekening, tijdstempeling of long-term validation. Dat zijn nabewerkingskwesties. De PAdES-ondersteuning van de Pro-editie is alleen de B-B-baseline en omvat geen B-T, B-LT of B-LTA; niets in deze bridge impliceert een tijdstempel- of LTV-capaciteit.
  • Het ondersteunt niet „alle Office-bestanden”. Het ondersteunt de zes opgesomde formaten en wijst al het overige af vóór enig verzoek.
  • /integrations/gotenberg/configuration/ — regels voor transportselectie en het volledige pin-model.
  • /integrations/gotenberg/production-usage/ — retries, time-outs, gelijktijdigheid en observability.
  • /integrations/gotenberg/troubleshooting/ — elke beveiligingsexception en de bijbehorende trigger.
  • /integrations/gotenberg/overview/ — de conversieflow en het dependency-model.