融資審査や経費精算のシステムで、アップロードされたPDFの真正性チェックをどう設計するか悩んでいる方に向けた内容です。オンプレのファイルサーバでPDFを保管していた業務をクラウド上のドキュメント管理基盤に移す際、改ざん検知のロジックをどこに置くかは意外と見落とされがちな論点です。
PDFファイルには、それを生成したソフトウェアの痕跡が残ります。目に見える透かしではなく、ファイル内部の構造的な癖のようなものです。この癖を読み取れば、「このファイルは名乗っている通りのソフトで作られたか」という、内容そのものとは独立した検証軸を持てます。銀行の明細書を装った改ざんPDFの検知など、審査・監査系のシステムを運用しているエンジニアには特に関係の深い話です。
PDFの中身は「ページの写真」ではない
PDFファイルは、ページを撮影した画像のようなものではありません。
フォントの指定・文字の配置命令・画像・ページ構造を記述したオブジェクトの木構造と、それらの位置を示す索引テーブル(クロスリファレンステーブルと呼ばれる目次のような仕組み)で構成された、小さなプログラムに近い存在です。
ページをこの構造に変換する過程には無数の細かい判断が必要で、生成に使うライブラリごとにその判断のクセが一貫して現れます。人の筆跡のようなものだとイメージすると分かりやすいはずです。同じ「残高」という文字を書いても、字形や間隔の取り方には個人差があります。PDFを吐き出すライブラリにも、そうした固有の書き癖があります。
ProducerとCreatorフィールドの正体
PDFのプロパティを開くと、ソフトウェア名を含む2つのメタデータが見つかります。
- Creator:人間が文書を作成する際に使ったアプリケーション(Word、InDesign、LaTeXなど)
- Producer:最終的なPDFバイト列を書き出したライブラリ(Adobe PDF Library、iText、ReportLabなど)
正常なパイプラインでは、この2つは筋の通ったストーリーを語ります。Wordで作成しPDF保存した文書なら、Creatorに「Microsoft Word」、Producerにも同様の文字列が入ります。LaTeXの論文ならCreatorが「TeX」、Producerが「pdfTeX-1.40.26」といった具合です。
ここで押さえておきたいのは、この2つのフィールドはファイル内部にただ書かれた文字列に過ぎないという点です。PDFフォーマット自体は、これらの値が真実であることを何も強制していません。16進エディタや数行のスクリプトがあれば、Producerの値は誰でも自由に書き換えられます。
つまり「Producer文字列が、その組織が使っているはずのシステム名と一致するか」という単純なチェックだけでは、雑な偽造は見抜けても、手慣れた偽造者には通用しません。ここが監視設計の出発点であり、終着点ではないという理解が重要です。
主要な生成ライブラリの「癖」を知る
本当に見るべきは、ファイルの残り部分がProducerの名乗る生成元と整合しているかどうかです。
代表的な例として、iText(Java/.NET向けのプログラム的PDF生成ライブラリ)があります。開発者がコードでオブジェクトを一つずつ組み立てる仕組みで、請求書プラットフォームや政府ポータル、契約書ツールなど企業システムで広く使われています。プログラム的に生成されるため、フォントの埋め込み方やオブジェクトツリーの並びに独特の規則性が出ます。
一方、デスクトップ編集ソフト(AdobeのAcrobatや各種フリーソフト)で一度出来上がったPDFを開いて再保存すると、その編集ソフト固有の構造的痕跡が新たに刻まれます。オリジナルの生成エンジンの痕跡と、後から加わった編集ソフトの痕跡が同一ファイル内に混在するため、この不整合こそが改ざんの手がかりになります。
主張されたProducer名と、実際のオブジェクト構造・フォント埋め込み方式・メタデータのデフォルト値が食い違っていれば、それは「制度的な帳票エンジンが出力した」という主張と「デスクトップ編集ソフトで再保存された」という実態が矛盾している証拠です。両方が同時に真実であることはあり得ません。
運用監視の観点で何を確認すべきか
この構造的不整合を検知する仕組みを、クラウド移行後の監視パイプラインにどう組み込むかを考えます。
オンプレでファイルサーバに置かれたPDFを人手で目視確認していた運用は、クラウド移行のタイミングで自動検査に置き換える好機です。監視設計としては、アップロード時のイベント(S3のPUTイベントやAzure Blob Storageのトリガーなど)を起点に、非同期でPDF構造解析ジョブを起動する構成が現実的です。
以下は現場で確認できる具体的なチェックポイントです。
- Producer/Creatorフィールドの文字列と、既知のライブラリ・製品の命名パターンとの一致度
- フォント埋め込み方式(サブセット化のルールはライブラリごとに固有)とProducerの主張の整合性
- オブジェクトツリーの並び順やクロスリファレンステーブルの形式が、名乗るライブラリの生成パターンと合うか
- 同一ファイル内に複数世代の生成痕跡(元の生成エンジンと後からの編集ソフト)が混在していないか
これらはpikepdfやpypdfのようなPythonライブラリでオブジェクト構造を取り出して比較できますし、qpdf --checkのようなコマンドでファイルの構造的な健全性を機械的に検査することも可能です。
# PDFの内部構造をテキストで確認する例
qpdf --qdf --object-streams=disable input.pdf output_readable.pdfこれで出力されたファイルをテキストエディタで開けば、オブジェクトの並びやメタデータの生値を直接観察できます。障害対応やインシデント調査の場面では、疑わしいファイルをこの形式に変換してから差分を取る手順が有効です。
根本原因分析とアラート設計への組み込み
改ざん検知を単発のスコアリングで終わらせず、運用の障害対応フローに組み込む視点も欠かせません。
たとえば審査システムで「Producer不一致」の検知件数が急増した場合、それは特定の攻撃キャンペーンの兆候かもしれませんし、逆に正規のシステム更新(帳票エンジンのバージョンアップなど)による偽陽性の急増かもしれません。ここを切り分けるには、監視ダッシュボードでProducer文字列ごとの検知件数を時系列で可視化し、リリース履歴と突き合わせる運用が現実的です。
クラウドの運用コストの観点では、この種の構造解析ジョブは同期処理でユーザーを待たせる必要はありません。キューイングして非同期実行し、結果を後からステータス更新する設計にすれば、審査担当者の体験を損なわずにコストも抑えられます。Lambda やCloud Functionsのような従量課金のサーバーレス環境との相性も良い処理です。
まとめ
PDFのProducer・Creatorフィールドは、名乗るだけなら誰でも書き換えられる自己申告に過ぎません。
真に見るべきは、フォント埋め込み方式やオブジェクトツリーの構造が、その名乗りと整合しているかどうかです。クラウド移行のタイミングでアップロードイベント起点の非同期検査を組み込み、qpdfなどのツールで構造を可視化する運用を検討してみてください。
検知件数の急増を見たときは、攻撃の兆候か正規システムの更新かをリリース履歴と突き合わせて切り分ける運用ルールも、あらかじめ決めておくと安心です。