コンテンツにスキップ

契約 / セキュリティポリシー

security-policy ドメインは、デフォルトで拒否する 3 つのコントラクトで構成されます。CryptoPolicyInterface はアルゴリズムと鍵の選択を制御し、HtmlSecurityPolicyInterface は HTML の機能範囲を制限し、ExternalResourcePolicyInterface はリモートリソースの読み込みを管理します。いずれもコントラクトであるため、デプロイ側はフォークすることなく、より厳格なポリシーを提供できます。

Terminal window
composer require nextpdf/core:^3

CryptoPolicyInterface は暗号処理のゲートです。Core は署名、暗号化、ハッシュ化のどの処理を行う前にも、このコントラクトに問い合わせます。このチェックは、ハッシュ、署名 OID、暗号、鍵強度を対象とします。このコントラクトは、監査ログ向けに最小ハッシュとポリシー名も返します。これにより、FIPS 140-3 や eIDAS などのルールセットを適用できます。署名および暗号化のコードを変更する必要はありません。ポリシーが設定されていない場合、すべてのアルゴリズムが許可されます。規制対象のサイトでは、明示的なポリシーの設定が必要です。

HtmlSecurityPolicyInterface は HTML の解析レイヤーで動作します。コンテンツがいずれかのレンダラーに到達する前に実行されます。タグ、属性、CSS プロパティ、URL スキームが許可されるかどうかを判定します。また、入力サイズとネストの深さに上限を設けます。サイズ制限と CSP ヘッダーを設定する、レンダラーごとのトランスポートポリシー(Chrome、Cloudflare、Gotenberg)と連携します。HTML ポリシーは、解析レイヤーの攻撃対象領域を縮小します。除去されたタグがレイアウトに到達することはありません。したがって、注入された要素が出力を変更することはできません。ポリシーが設定されていない場合、デフォルトではすべての機能セットが許可されます。

ExternalResourcePolicyInterface は、HTML パイプラインが外部のフォント、スタイルシート、画像を取得できるかどうかを判定します。また、各取得に対する上限も設定します。デフォルトではすべて拒否します。すべてのオプションは、有効化するまでオフのままです。このコントラクトは最小権限の考え方を採用します。信頼できない HTML は、攻撃者の URL を指している可能性があります。@font-face の取得は、スキーム、サイズ、グリフ数で制御します。@import は、スキーム、深さ、合計サイズで制御します。background-image は、スキームリストと完全一致のドメイン許可リストで制御します。data URI のサイズに上限を設けます。また、SVG の外部参照も制御します。このコントラクトは、本番環境では常に SVG の外部参照を拒否する必要があることを定めています。それらはリクエスト偽造とスクリプトインジェクションを可能にします。無制限の URL 取得は、サーバーサイドリクエストフォージェリの経路となります。OWASP Top 10 2025 によると、URL の変更によってアクセス制御が回避される可能性があります。コンポーネントは、安全なリンクを介して公式なソースからのみ取得する必要があります。

種別主なメンバー安定性導入バージョン
CryptoPolicyInterfaceinterfaceisHashAlgorithmAllowed(), isSignatureAlgorithmAllowed(), isEncryptionAlgorithmAllowed(), isKeyStrengthAllowed(), getPreferredHashAlgorithm(), getName()stable1.9.0
HtmlSecurityPolicyInterfaceinterfaceisTagAllowed(), isAttributeAllowed(), isCssPropertyAllowed(), isUrlSchemeAllowed(), getMaxInputSize(), getMaxNestingDepth(), getName()stable3.1.0
ExternalResourcePolicyInterfaceinterfaceisFontFaceAllowed(), getAllowedFontSchemes(), getMaxFontFileSize(), getMaxFontGlyphs(), isImportAllowed(), getMaxImportDepth(), isBackgroundImageAllowed(), getAllowedImageDomains(), getMaxDataUrlSize(), isSvgExternalReferenceAllowed()stable4.0.0

ExternalResourcePolicyInterface が返す境界値は型付きです。サイズは positive-int、インポート深さは int<1, 100>、スキームおよびドメインのリストは list<non-empty-string> です。デフォルトの実装は、すべての機能を拒否します。

examples/contracts/security-policy-quickstart.php
<?php
declare(strict_types=1);
require_once __DIR__ . '/../../vendor/autoload.php';
use NextPDF\Contracts\HtmlSecurityPolicyInterface;
/**
* Decide whether a tag survives the policy.
*
* @param HtmlSecurityPolicyInterface $policy A core or custom policy.
*/
function tagSurvives(HtmlSecurityPolicyInterface $policy, string $tag): bool
{
return $policy->isTagAllowed($tag);
}

この関数はコントラクトに依存します。制限的なポリシーでもデフォルトのポリシーでも、この依存関係を満たせます。

examples/contracts/security-policy-production.php
<?php
declare(strict_types=1);
require_once __DIR__ . '/../../vendor/autoload.php';
use NextPDF\Contracts\CryptoPolicyInterface;
use NextPDF\Contracts\ExternalResourcePolicyInterface;
use NextPDF\Contracts\HtmlSecurityPolicyInterface;
use Psr\Log\LoggerInterface;
final readonly class UntrustedHtmlGate
{
public function __construct(
private HtmlSecurityPolicyInterface $htmlPolicy,
private ExternalResourcePolicyInterface $resourcePolicy,
private CryptoPolicyInterface $cryptoPolicy,
private LoggerInterface $logger,
) {}
/**
* Reject input that exceeds the configured limits before rendering.
*
* @param string $html Untrusted HTML markup.
*/
public function assertAcceptable(string $html): void
{
$maxInput = $this->htmlPolicy->getMaxInputSize();
if ($maxInput > 0 && \strlen($html) > $maxInput) {
$this->logger->warning('HTML rejected: input over limit', [
'policy' => $this->htmlPolicy->getName(),
'limit' => $maxInput,
]);
throw new \LengthException('HTML input exceeds policy limit.');
}
if ($this->resourcePolicy->isSvgExternalReferenceAllowed()) {
$this->logger->error('Unsafe policy: SVG external references enabled.');
throw new \LogicException('SVG external references must be denied in production.');
}
}
}

このゲートは、パイプラインの実行前に入力上限を適用し、安全でないリソースポリシーを拒否します。監査用にポリシー名を記録し、具体的な例外を再スローします。

  • CryptoPolicyInterface は、ポリシーが設定されていない場合、すべてのアルゴリズムを許可します。この寛容なデフォルトは開発時の利便性であり、本番環境の構成ではありません。規制対象のデプロイでは、明示的なポリシーを設定してください。
  • HtmlSecurityPolicyInterface::getMaxInputSize() は、無制限を表す 0 を返します。0 は「すべて拒否」ではなく「ポリシーによる制限なし」として扱い、トランスポート層の上限も併せて適用してください。
  • ExternalResourcePolicyInterface は、デフォルトですべて拒否します。スキームリストを設定せずに @font-facebackground-image を有効にすると、リクエスト偽造の攻撃対象領域が広がります。機能を有効にする際は許可リストを設定してください。
  • 空のドメイン許可リストを getAllowedImageDomains() に設定すると、背景画像が有効になった時点で、すべてのドメインが許可されます。空のリストは拒否を意味しません。明示的にドメインを指定してください。
  • isSvgExternalReferenceAllowed() は、本番環境では false を返すべきです。コントラクトはこれを文書化しています。true を返すポリシーは、構成上の選択ではなく検出事項です。

ポリシーチェックは述語呼び出しであり、O(1) です。入力に比例するコストはありません。ポリシーは、解析中にタグごと、属性ごと、CSS プロパティごと、URL ごとに参照されます。極端なドキュメントでは、呼び出し回数が増大します。各呼び出しは定数時間のままです。壁時計時間 1500 ms、ピーク 64 MB の performance_budget は、ポリシー評価ではなく解析とレンダリングに支配されます。入力サイズとネスト深さの上限は、パーサー自体のコストを抑えるために存在します。厳格なポリシーは、過大またはネストの深いドキュメントをレイアウトの前に拒否することで、最悪ケースのパフォーマンスを改善します。

これらのコントラクトはエンジンの防御境界であり、脅威モデルは明示されています。アルゴリズムのダウングレードは CryptoPolicyInterface によって緩和されます。このインターフェイスは、いかなる操作の前にも弱いハッシュと短い鍵をブロックします。PDF へのクロスサイトスクリプティングおよびコンテンツインジェクションは HtmlSecurityPolicyInterface によって緩和されます。このインターフェイスは、レンダラーが実行される前に、解析レイヤーで許可されないタグ、属性、CSS を除去します。サーバーサイドリクエストフォージェリ、解凍爆弾、累積サイズ爆弾は ExternalResourcePolicyInterface によって緩和されます。このインターフェイスは、デフォルトですべて拒否し、すべての取得をスキーム、サイズ、深さ、ドメインで制限します。リソース枯渇は、入力サイズ、ネスト深さ、フォントグリフ、インポート深さの上限によって緩和されます。各ポリシーはコントラクトであるため、デプロイ側はエンジンをフォークすることなく境界を強化でき、ポリシー名は監査ログ用に公開されます。すべての HTML、すべての URL、すべてのフォントおよび画像のバイト列を敵対的なものとして扱ってください。このページには export_control_class: legal-review-required が付与されています。コントラクトが暗号ポリシーを管理するためです。本文はすべての規範的なソースを言い換えており、いずれも引用していません。

主張標準条項証拠
制約のない URL 処理では、URL の変更によってアクセス制御が回避される可能性がありますが、外部リソースポリシーはすべて拒否のデフォルトと完全一致のドメイン許可リストによってこれを緩和します。OWASP Top 10 2025A01
外部コンポーネントは、安全なリンクを介して公式なソースからのみ取得する必要があり、ポリシーはこの要件をスキーム許可リストによって強制します。OWASP Top 10 2025ソフトウェアサプライチェーン

いずれの点も OWASP ガイダンスを言い換えたものです。OWASP の資料は条項で参照しており、エンジンはそのテキストを再現しません。

Core は 3 つのポリシーコントラクトを定義して固定し、開発向けには寛容なデフォルト、すべて拒否のリソースポリシーには厳格なデフォルトを提供します。Enterprise エディションは、CryptoPolicyInterface の背後に FIPS 140-3 プロファイルを提供するため、規制対象のデプロイは、署名や暗号化のコードを変更することなく、検証済みのアルゴリズム構成を得られます。コントラクトのサーフェスは、エディション間で同一です。違いは、デプロイ側が注入するポリシーの実装です。