💡 完全な動作例はGitHubで入手可能です:
skip-external-resources-when-signing-dotnet
はじめに
Word 文書には、ファイル内に存在しない画像が含まれることがあります。文書はアドレスを保持しており、開く側がそのアドレスにアクセスして画像を取得します。デスクトップ環境ではこれは機能であり、ソースが更新されれば画像も更新されます。アップロードを受け付けるサーバーでは、ファイルを送ってきた人がインフラがリクエストする URL を決定できてしまいます。
安全な文書読み込みは .NET 用 GroupDocs.Signature の動作で、これらのリクエストを行わないようにします。バージョン 26.9 以降、LoadOptions.SkipExternalResources の既定値は true です。本記事では、同一文書に対して 3 つの読み込みモードを比較し、すべてのホストを許可せずに特定のホストだけを許可する方法を示し、信頼できないファイルに署名する際にネットワークアクセスが全く不要である理由を解説します。
なぜ重要なのか
この攻撃は「サーバーサイドリクエストフォージェリ(SSRF)」と呼ばれ、具体的に 3 つの形態があります。
- インターネットからは到達できない内部アドレスがサーバーからは到達可能であり、細工された文書が
http://169.254.169.254/やローカルホスト上の管理エンドポイントにアクセスさせ、結果に応じて情報漏洩させることができる。 - 文書内の UNC パスが Windows ホストに対して外部認証を促し、攻撃者が管理するサーバーに資格情報を渡す可能性がある。
- 応答しないホストへのリンクは、タイムアウトまでロードスレッドを占有し、ワーカープールを消耗させる安価な手段になる。
最初は理論的な懸念だと思っていましたが、テスト文書が全く外部リクエストを行うべきでないサービスを通じて画像を取得するのを目の当たりにしました。これは文書ライブラリのバグが必要なわけではありません。リンクをたどるのはフォーマットの要求であり、サーバーがそれに応じるかどうかが問題です。
方法 1 – 新しいデフォルト
LoadOptions を全く使用しない場合:
using var signature = new Signature(sourcePath);
return SavePagePreview(signature, previewPath);
何も取得されません。プレビューはリンクされた画像の代わりに空のプレースホルダーを描画し、PNG のサイズは本来より小さくなります。このサイズ差が、リクエストがマシンを離れなかったことを示す最も手軽な証拠です。
外部とみなされるのはどの機能か? 埋め込みではなくリンクされた画像、INCLUDEPICTURE フィールド、プレゼンテーションやスプレッドシート内のリンク画像、SVG が参照する画像やスタイルシートです。埋め込みコンテンツはそのままです——すでにファイル内にあります。
方法 2 – アドレスをホワイトリストに登録
多くの文書は正当な場所にリンクしています:企業 CDN、社内画像サーバー、テンプレートストアなど。これらだけを許可し、他はブロックします。
var loadOptions = new LoadOptions
{
WhitelistedResources = new List<string> { trustedAddress }
};
using var signature = new Signature(sourcePath, loadOptions);
マッチングルールに注意が必要です。リソースアドレスに対する大文字小文字を区別しない部分文字列テストなので、短いフラグメントは危険です。たとえば github は github.attacker.example/payload.png にもマッチします。スキーム、ホスト、パスを指定してください——サンプルは raw.githubusercontent.com/groupdocs-signature/ をホワイトリストに登録しています。
方法 3 – すべて許可
26.9 以前の動作で、まだ利用可能です。
var loadOptions = new LoadOptions { SkipExternalResources = false };
自前のアプリケーションが生成した文書には合理的です。注意すべき罠は、廃止予定の LoadExternalResources プロパティが逆の意味を持つことです。SkipExternalResources = false は LoadExternalResources = true の代わりになります。古いプロパティから値をコピーすると、エラーなしでセキュリティ姿勢が逆転してしまいます。
3 つのモードの比較 – 使い分け
| モード | 推奨シーン | 主な利点 | 制限事項 |
|---|---|---|---|
| デフォルト(スキップ) | ユーザーアップロード、メール、パートナーファイル | 外部リクエストが一切発生しない | リンク画像はプレースホルダーとして表示される |
| ホワイトリスト | 自社が所有するホストにリンクする文書 | 正当なリンクは機能し続ける | 部分文字列マッチングのため、長く具体的なフラグメントが必要 |
| すべて許可 | 自社システムが生成したファイル | プレビューが元通りに表示される | デフォルトで除去された SSRF リスクが復活する |
署名時にリソースは必要か?
必要ありません。これが実用的なメリットです。QR コード署名はデフォルトのロード設定で適用され、文書の読み込み・署名・保存時に外部リソースは一切リクエストされません。
var options = new QrCodeSignOptions("Approved by GroupDocs.Signature")
{
EncodeType = QrCodeTypes.QR,
Left = 400,
Top = 50,
Width = 120,
Height = 120
};
SignResult result = signature.Sign(outputPath, options);
署名された出力はリンクを保持するため、後でユーザーが文書を開くと自分のマシンで画像が解決されます。スキップはサーバー側のポリシーであり、文書自体を編集するものではありません——これが他者のファイルを扱う際に安全に適用できる理由です。
アップグレード時に変わること
多くのサービスでは、最初は目に見える変化はありません。これは重要なポイントで、動作が全体的に変わるセキュリティデフォルトはアップグレード審査で問題になる可能性があるためです。例外は、プレビューやサムネイルがリンク画像を表示していたものがプレースホルダーに変わるケースです——これは期待通りの変更であり、ホストが自社所有であればホワイトリストに追加し、外部からの文書であればそのまま受け入れます。
サンプルが使用している確認方法はシンプルです:3 つのモードすべてで同一文書をレンダリングし、出力サイズを比較します。デフォルトとホワイトリストプレビューのサイズが同一であれば、どちらでもリクエストは発生していません——通常はホストがそのマシンから到達不能であることを示します。サンプルはその旨をヒントとして出力します。
プレビュー ヘルパー – 明示的でない理由
上記 3 つのモードのうち 2 つは小さなヘルパーを呼び出します。PreviewOptions がパスを受け取らないため、以下のように示します。
var previewOptions = new PreviewOptions(
pageData => File.Create(previewPath),
(pageData, pageStream) => pageStream.Dispose())
{
PreviewFormat = PreviewOptions.PreviewFormats.PNG
};
signature.GeneratePreview(previewOptions);
2 つのストリームファクトリを受け取ります——ページごとにストリームを作成するものと、解放するものです。サンプル文書は 1 ページだけなので 1 ファイルが書き込まれます。複数ページの場合はファイル名にページ番号を入れるか、すべてのページが最後のページで上書きされてしまいます。
ベストプラクティス
- 自分で生成していないものはすべて信頼できないものとして扱い、セキュリティが高いパートナーからのファイルであっても同様に扱う。
- ホワイトリストのフラグメントは曖昧さがないよう十分に長くし、CDN が変更されたときは見直す。
LoadExternalResourcesに割り当てていた値をそのままSkipExternalResourcesに設定しない。- 設定だけでなく、出力サイズで実際にリクエストが行われたかを検証する。設定が正しくてもリクエストが発生しなかったという主張は別物です。
SVG に関して
別途言及すべき点です。SVG はアップロード形式としても SSRF ベクトルとしても一般的です。SVG は URL で画像やスタイルシートを参照でき、これらも同じルールの外部リソースです——デフォルトでスキップ、ホワイトリスト化、復元可能です。サーバー側で SVG アバターやロゴを受け取りレンダリングするサービスは、この変更が保護する典型的なケースです。
ユーザーからの SVG を受け入れるパイプラインがある場合、デフォルト設定が望ましいです。自社テンプレートが自社ホスト上の共有スタイルシートを取得するケースだけはホワイトリストに追加します。
結論
デフォルトが変更されたことで、リスクのある動作は明示的な決定が必要になり、安全な動作は何もしなくても有効になります。信頼できない入力にはデフォルトを維持し、自社ホストが関与する場合は絞り込んだホワイトリストを設定し、署名自体はネットワークを一切必要としないことを覚えておいてください。サンプルを自分の文書で実行すれば、1 分程度で 3 つのファイルサイズが示す通り、サービスが何を取得しているかが分かります。