コンテンツにスキップ

NextPDF Gotenberg のセキュリティと運用

このブリッジは、アプリケーションが保持しているドキュメントをネットワーク経由で外部サービスに送信します。そのため、サーバーサイドリクエストとトランスポートセキュリティの両方で攻撃面になります。このパッケージは、その両方の攻撃面に対して具体的で検証可能な制御を実装しています。ただし、それ単体でシステムを安全にするわけではありません。これらの制御は、Gotenberg サービスをどのようにデプロイし、運用するかと組み合わせて初めて有効になります。このページでは、実装されている制御と、それを成立させる運用上の責務について説明します。

ここに挙げる制御はいずれも、保証として提示するものではありません。どれも、明示された限界を持つ、定義済みでテストに裏付けられた挙動です。

ブリッジには 2 つの異なるセキュリティポリシーがあり、それぞれ異なるレイヤーで機能します。

  • トランスポートポリシーGotenbergSecurityPolicy)— URL スキームの強制、サーバーサイドリクエストフォージェリ(SSRF)スクリーニング、DNS リバインド防御、入力サイズ制限、ファイル名スクリーニング。以下で詳しく説明するレイヤーです。
  • HTML パースポリシー — パースレイヤーのコンテンツポリシーです。デフォルトでは NextPDF コアのデフォルトポリシーが適用され、コンテンツがレンダラーに到達する前に実行されます。これはトランスポートポリシーを補完するものであり、それとは独立しています。このページが扱うのはトランスポートポリシーです。

サーバーサイドリクエストフォージェリ(SSRF)スクリーニング

「サーバーサイドリクエストフォージェリ(SSRF)スクリーニング」という見出しのセクション

設定された API URL は、1 バイトもプロセス外へ出る前にスクリーニングされます。この制御は 3 つの部分で構成されます。

スキームの強制。 受け入れられるのは https のみです(大文字小文字は区別しません)。素の http:// URL は拒否されます。そのため、すべての変換とヘルスプローブで Transport Layer Security が必須になります。

アドレススクリーニング。 ホストが IP リテラルの場合、プライベート範囲または予約済み範囲に該当すれば拒否されます。ホストが名前の場合は、その A レコードと AAAA レコードのすべてに解決され、解決されたアドレスのいずれかがプライベートまたは予約済みであれば、リクエストは拒否されます。単一のアドレスではなくレコードセット全体を解決することが、パブリックアドレスも返す名前の背後にプライベートアドレスを隠そうとする攻撃を防ぐための制御です。これは、ドメイン名の背後にあるすべての IP アドレス(IPv4 および IPv6 の A レコードと AAAA レコード)を取得し、それぞれを許可リストと照合して検証する、という OWASP の SSRF 防止ガイダンスで説明されているアプローチです(OWASP Cheat Sheet Series、SSRF 防止、アプリケーション層の防御。本ページの RAG サイドカーにピン留め)。

Time-of-check/time-of-use(TOCTOU)の再チェック。 検証時に取得したアドレスセットは、リクエストの直前に再解決され、比較されます。新しいアドレスが出現した場合、リクエストは DNS リバインドエラーで中止されます。これにより、リバインド攻撃が悪用しうる、検証チェックと接続の間のウィンドウが閉じられます。

ブリッジが cURL ピン留めトランスポートを使用する場合、検証済みのアドレスセットは CURLOPT_RESOLVE によって接続にバインドされます。これにより、カーネルは、接続時の新たな DNS ルックアップが返しうるアドレスではなく、審査済みのアドレスへ接続します。そのトランスポートではリダイレクト追従が無効化されており(CURLOPT_FOLLOWLOCATION はオフ、CURLOPT_MAXREDIRS はゼロ)、3xx がリクエストを審査されていないホストへ密かに送ることはできません。代わりに、そのレスポンスはポリシーレイヤーへ引き渡されます。

運用上の帰結。 SSRF ガードは設計上、プライベートアドレスおよび予約済みアドレスを拒否します。Gotenberg がプライベートネットワーク上で動作している場合、ブリッジをそのプライベートアドレスに向けることはできません。ガードが受け入れるアドレスを通じて公開し、後述のデプロイの項で説明するように、その経路をネットワークセグメンテーションと認証で保護してください。

cURL ピン留めトランスポートでは、TLS のピア検証とホスト検証が常に有効です(CURLOPT_SSL_VERIFYPEER は true、CURLOPT_SSL_VERIFYHOST は 2)。標準のチェーン検証に加えて、ブリッジは SubjectPublicKeyInfo(SPKI)ピン留めをサポートします。

各ピンはサーバーの SubjectPublicKeyInfo の SHA-256 ハッシュであり、sha256/<base64> として表現されます。ブリッジは、SPKI ハッシュがプライマリピンとバックアップピンを合わせたセット内のいずれかのピンに一致する証明書を受け入れます。このバックアップピンモデルは RFC 7469 §4.3 に従っています。同節は、バックアップピン(まだデプロイされていない第二の鍵ペアのフィンガープリント)を、不意のピン検証失敗から回復するための主要な手段として位置づけています。また §2.5 は、ピン留めされたセットに、現在の証明書チェーンに存在しないピンを少なくとも 1 つ含めることを要求しています(RFC 7469 §4.3 および §2.5。本ページの RAG サイドカーにピン留め)。ブリッジのコードは、少なくとも 1 つのバックアップピンと、結合セットの交差のセマンティクスについて RFC 7469 §2.1 および §2.6 を宣言しています。ピン留めはオプトインです。ピンが 1 つも設定されていない場合は標準のチェーン検証が適用され、ピン留めは強制されません。

パースできないピンは、リクエストが行われる前に設定エラーを発生させます。SPKI が設定済みのどのピンにも一致しない実稼働中の証明書は、設計上、トランスポートによりリクエストが失敗します。

ローテーションに失敗すると、ブリッジはサービスから締め出されます。停止を発生させずにローテーションするには、次のようにします。

  1. サーバーの鍵を変更する前に、新しい鍵の SPKI ピンを生成し、バックアップピンのリストに追加します。その設定をデプロイします。これでブリッジは、現在の鍵と将来の鍵の両方を受け入れます。
  2. サーバー証明書または鍵を新しい鍵へロールします。
  3. 変換が引き続き成功することを確認します(新しい鍵はバックアップピンに一致する状態になっています)。
  4. 新しいピンをバックアップリストからプライマリリストへ移し、引退した鍵のピンを削除します。デプロイします。
  5. 次回のローテーション用のピンを生成して新しいバックアップとして用意し、セットが常に使用可能な予備を備えるようにします。

バックアップリストをプライマリリストとは別に保つことで、有効なピンに触れずに次のピンを用意して検証できます。

apiKey が空でない場合、その値は変換リクエストの Authorization: Bearer ヘッダーとして送信されます。このフィールドには #[\SensitiveParameter] が付与されているため、その値はスタックトレースから秘匿されます。ブリッジがシークレットを取得するわけではありません。プロセス起動時にシークレットマネージャーから供給し、決してコミットしないでください。トークンはリクエストログに書き込まれません。ログに記録されるデバッグエントリには、URL、ファイル名、フォーマット、コンテンツ長のみが含まれます。

レスポンスは、ステータスが 200 であり、Content-Typeapplication/pdf が含まれ、ボディが %PDF シグネチャで始まる場合にのみ受け入れられます。バイトシグネチャをチェックするのは、宣言されたコンテンツタイプだけでは、そのバイト列が何であるかを確定できないためです。これは、WHATWG MIME Sniffing 標準が MIME タイプスニッフィングアルゴリズムで形式化している論理と同じです。同アルゴリズムは、提供されたタイプではなく先頭バイトパターンとのマッチングから計算上のタイプを導き出します。OWASP のファイルアップロードガイダンスは、対応するアプリケーション原則を次のように述べています。ファイルの種類を検証し、宣言された Content-Type ヘッダーは偽装されうるため信頼しないこと(WHATWG MIME Sniffing §6.2.3。OWASP Cheat Sheet Series、ファイルアップロード検証。いずれも本ページの RAG サイドカーにピン留め)。ブリッジは、これと同等のチェックを受信側で防御的に適用します。不一致があれば型付き例外が発生し、そのバイト列が結果として返されることは決してありません。

この境界は、ここで PSR-18 のコントラクトが重要になる理由でもあります。PSR-18 クライアントは、リクエストを送信できない場合、またはレスポンスを PSR-7 オブジェクトにパースできない場合にのみ例外をスローします。エラーステータスコードに対しては例外をスローしません。整形式の 4xx/5xx レスポンスは、通常どおり呼び出し元に返されます(PSR-18、「Exceptions」。本ページの RAG サイドカーにピン留め)。したがってブリッジは、返されたレスポンスが成功したと想定せず、ステータス、タイプ、シグネチャを自ら検査します。content-type 制約違反に対する HTTP セマンティクス —— サーバーがサポート外のフォーマットのコンテンツを拒否する 415(Unsupported Media Type)による拒否 —— が、受信側チェックが従うモデルです(RFC 9110 §15.5.16。本ページの RAG サイドカーにピン留め)。

プロセス内で唯一のリソース上限は maxFileSize(デフォルト 52,428,800 バイト = 50 MiB)で、リクエスト前に強制されるため、サイズ超過の入力がサービスに到達することはありません。ブリッジには、組み込みの同時実行数制限、レート制限、出力サイズ上限はありません。これらはデプロイと呼び出し元の責務です(/integrations/gotenberg/production-usage/ を参照)。maxFileSize は、実際のドキュメントが必要とする最小の値に設定してください。上限が厳しいほど、より低コストなサービス拒否(DoS)対策になります。

ブリッジの安全性は、それが呼び出すサービスの安全性を超えることはありません。そのサービスを運用するのはあなたです。以下の責務が、上記の制御を完成させます。

  • Gotenberg の前段で TLS を終端する。 Gotenberg コンテナは、デフォルトでは素の HTTP で通信します。ブリッジは HTTPS を要求するため、Gotenberg を TLS 終端を行うリバースプロキシ、Ingress、またはサービスメッシュの背後に配置し、ブリッジを HTTPS エンドポイントに向けてください。ピン留めを有効にする場合は、プロキシの SPKI をピン留めしてください。
  • Gotenberg を公開しない。 Gotenberg は LibreOffice や Chromium 系のエンジンでドキュメント変換を行うものであり、インターネットに面するサービスではありません。ネットワークポリシーまたはファイアウォールにより、Ingress へのアクセスを、Gotenberg を呼び出すアプリケーションホストに制限してください。
  • 経路上で認証を要求する。 ブリッジは設定されていればベアラートークンを送信します。認証されていないリクエストが変換エンジンに到達できないよう、プロキシでそのトークン(または相互 TLS)を強制してください。
  • 特定のサービスバージョンに固定する。 ブリッジは、ちょうど 2 つのサービスパス —— /forms/libreoffice/convert/health —— を前提としています。Gotenberg イメージを特定のパッチタグに固定し、デプロイするバージョンでその 2 つのパスを検証し、アップグレードのたびに再検証してください。
  • 変換キャパシティを意図的にサイジングする。 各変換は、リクエスト中にワーカーを 1 つ占有します。Gotenberg のデプロイをピーク時の同時変換レートに合わせてサイジングし、呼び出し元側でも実行中の変換数をそれに合わせて制限してください。キャパシティは、このパッケージではなく、あなたのデプロイに属する性質です。
  • 変換入力を信頼できないものとして扱う。 変換に渡されるドキュメントは、複雑なエンジンで処理されます。maxFileSize を制約し、Gotenberg のデプロイを隔離し(専用のネットワークセグメント、最小限の egress、内部サービスへのアクセスなし)、エンジンにパッチを適用し続けてください。
  • これは「デフォルトで安全」ではありません。制御は本物ですが、正しいデプロイと設定に依存します。
  • 変換を「改ざん不可能」にしたり、出力を「認証済み」にしたりするものではありません。トランスポートとレスポンスの形状は検証しますが、ドキュメントの内容を保証するものではありません。
  • 署名、タイムスタンプ、長期検証(LTV)を提供するものではありません。それらは後処理の関心事です。Pro エディションの PAdES サポートは B-B ベースラインのみであり、B-T、B-LT、B-LTA は含みません。このブリッジのいかなる要素も、タイムスタンプや LTV の機能を含意するものではありません。
  • 「すべての Office ファイル」をサポートするものではありません。列挙された 6 つのフォーマットをサポートし、それ以外はすべて、リクエストが行われる前に拒否します。
  • /integrations/gotenberg/configuration/ — トランスポート選択のルールとピンモデルの全体像。
  • /integrations/gotenberg/production-usage/ — リトライ、タイムアウト、同時実行、可観測性。
  • /integrations/gotenberg/troubleshooting/ — すべてのセキュリティ例外と、その発生要因。
  • /integrations/gotenberg/overview/ — 変換フローと依存関係モデル。