コンテンツにスキップ

セキュリティ: 暗号化、暗号ポリシー、署名サーフェス

Core セキュリティモジュールは、AES-256 ドキュメント暗号化を適用し、すべてのアルゴリズム選択を暗号ポリシー契約でゲートし、デプロイメントが鍵管理サービスを接続するための統合ポイントを公開します。実効的なドキュメント保護は、鍵の取り扱い、パスワードの強度、読み込み側のリーダー、デプロイメント環境に依存します。このページでは、それぞれの境界を明確に示します。

Terminal window
composer require nextpdf/core:^3

セキュリティモジュールは、3 つのサーフェスをまとめています。1 つ目は暗号化です。ドキュメントの setEncryption() エントリポイントが、AES-256 標準セキュリティハンドラを構成します。2 つ目は暗号ポリシーゲートです。CryptoPolicyInterface が、デプロイメントで許可するハッシュ、署名、暗号、鍵強度を決定します。3 つ目は署名サーフェスです。このページでは参照にとどめ、内容は説明しません。署名を参照してください。

暗号化には、ISO 32000-2:2020 §7.6 で定義されている AES-256 を使用します。デフォルトのパスは、AESV3 暗号フィルターを備えた V=5 / R=6 標準セキュリティハンドラです。ファイル鍵のバイト長は 32 バイト(256 ビット)で、FIPS 197 と一致します。オプトインのパスでは、ISO/TS 32003:2023 V=6 / R=7 AES-256-GCM 認証付き暗号化を追加します。詳細ページでは、両方を説明しています。暗号化を参照してください。

暗号ポリシーゲートは、拒否または許可を判断する述語です。Core は、署名、暗号化、ハッシュ処理のどのステップでも、その前に CryptoPolicyInterface を参照します。ポリシーが設定されていない場合、Core はすべてのアルゴリズムを許可します。これは開発に適したオープンなデフォルトであり、本番環境向けの構成ではありません。規制対象のデプロイメントでは、明示的なポリシーを設定する必要があります。Contracts / Security Policyでは、契約サーフェスについて説明しています。

パーミッションフラグは、過大に説明されやすいポイントであるため、このページでは明確に扱います。パーミッションビットマスクは、暗号化された /Perms エントリと /P 値に格納されます。準拠リーダーは、これらの制限を尊重することが期待されます。これらのフラグは、暗号によって強制されるものではありません。これらのビットを無視するプロセッサーは、復号鍵を取得すれば、依然としてコンテンツの読み取り、コピー、変更を行えます。パーミッションフラグに依存する関係者には、必ずこの制限を明示してください。

鍵管理と PKCS#11 統合は、契約上のポイントです。Core は、ローカル鍵パスを提供します。KeyMaterial 値オブジェクトは、長さを検証した 256 ビット鍵をラップし、その文字列表現およびデバッグ表現を通じた開示を防ぎます。HSM および PKCS#11 の鍵保管パスは Enterprise の機能であり、同じ契約でゲートされます。このページでは統合ポイントの名称を示しますが、Enterprise 実装については説明しません。

種別主要メンバー安定性導入バージョン
Document::setEncryption()メソッド(関心事 HasSecurityuserPassword, ownerPassword, permissions安定版1.0.0
Document::useAesGcm()メソッド(関心事 HasSecurity?bool $enabled — オプトインの ISO/TS 32003 V=6/R=7安定版2.18.0
Aes256Encryptorクラスencrypt(), decrypt(), buildEncryptionDictionary(), verifyUserPassword(), verifyOwnerPassword(), validatePerms()安定版1.0.0
Aes256GcmEncryptorクラスencrypt(), decrypt(), encryptStream(), assertWithinSafetyBound(), invocationCount()安定版2.18.0
KeyMaterialfinal readonly クラスgenerate(), exposeKey(), fingerprint()安定版2.18.0
CryptoPolicyInterfaceインターフェイスisHashAlgorithmAllowed(), isSignatureAlgorithmAllowed(), isEncryptionAlgorithmAllowed(), isKeyStrengthAllowed(), getPreferredHashAlgorithm(), getName()安定版1.9.0
Config::withCryptoPolicy()メソッドCryptoPolicyInterface $policy安定版1.9.0
CryptoCapabilitiesfinal クラスhasAesGcm(), detectFipsMode(), assertFipsAvailableForProfile()安定版2.0.0
examples/22-protection.php
<?php
declare(strict_types=1);
require_once __DIR__ . '/../vendor/autoload.php';
use NextPDF\Core\Document;
$doc = Document::createStandalone();
$doc->setTitle('Encrypted Document — Restricted Permissions');
// Call setEncryption() BEFORE addPage().
// Permission bit 3 (value 4) = printing allowed; all other operations denied.
$doc->setEncryption(
userPassword: 'demo',
ownerPassword: 'admin',
permissions: 4,
);
$doc->addPage();
$doc->setFont('helvetica', 'B', 20);
$doc->cell(0, 14, 'Encrypted PDF Document', newLine: true);
$doc->save(__DIR__ . '/output/22-protection.pdf');

ユーザーパスワードは、ドキュメントを開くためのものです。オーナーパスワードは、フルアクセスを付与します。パーミッションフラグが制約するのは、準拠リーダーのみです。

examples/security/policy-gated-encryption.php
<?php
declare(strict_types=1);
require_once __DIR__ . '/../../vendor/autoload.php';
use NextPDF\Contracts\CryptoPolicyInterface;
use NextPDF\Core\Document;
use Psr\Log\LoggerInterface;
final readonly class PolicyGatedEncryption
{
public function __construct(
private CryptoPolicyInterface $cryptoPolicy,
private LoggerInterface $logger,
) {}
/**
* Encrypt only when the active policy permits AES-256-CBC.
*
* @param non-empty-string $userPassword Opens the document.
* @param non-empty-string $ownerPassword Grants full access.
*/
public function protect(
Document $doc,
string $userPassword,
string $ownerPassword,
int $permissions,
): void {
if (!$this->cryptoPolicy->isEncryptionAlgorithmAllowed('aes-256-cbc')) {
$this->logger->error('Encryption refused by crypto policy', [
'policy' => $this->cryptoPolicy->getName(),
]);
throw new \RuntimeException('AES-256-CBC denied by the active crypto policy.');
}
$doc->setEncryption($userPassword, $ownerPassword, $permissions);
$this->logger->info('Document encrypted', [
'policy' => $this->cryptoPolicy->getName(),
'algorithm' => 'aes-256-cbc',
]);
}
}

このゲートは暗号化の前にポリシーを参照し、監査証跡のためにポリシー名をログに記録します。ポリシーが暗号を拒否した場合は、特定の例外で処理を拒否します。

  • 必ず setEncryption()先にaddPage() より前に呼び出してください。後から呼び出しても、ライターがすでに出力したコンテンツが遡って暗号化されることはありません。
  • PDF/A モードと暗号化は、相互に排他的です。ISO 19005 は、すべての PDF/A バリアントで Encrypt トレーラーキーを禁止しているため、PDF/A マネージャーがアクティブな場合、setEncryption()useAesGcm() は例外を送出します。
  • オーナーパスワードが空の場合、setEncryption() の内部でユーザーパスワードにフォールバックします。共有パスワードが 1 つだけのドキュメントでは、ユーザーパスワードの保持者にオーナーレベルのアクセスが付与されます。
  • CryptoPolicyInterface は、ポリシーが注入されていない場合、すべてのアルゴリズムを許可します。オープンなデフォルトは開発用の利便性として扱い、規制対象のデプロイメントでは必ず明示的なポリシーを設定してください。
  • パーミッションフラグは、リーダーに対する勧告的なものです。これらを、悪意のあるプロセッサーが回避できないアクセス制御として説明しないでください。

setEncryption() は、ドキュメントのビルド時に反復鍵導出ルーチン(Algorithm 2.B、revision 6)を実行します。このコストは制限されており、ドキュメントごとに一定で、ページ数に比例しません。オブジェクトごとの暗号化は、ストリームまたは文字列ごとに 1 回の AES 処理です。オプトインの AES-256-GCM パスは、オブジェクトごとに 28 バイトのオーバーヘッド(12 バイトの IV と 16 バイトのタグ)を追加し、大きなコンテンツを 16 MiB のチャンクでストリーミングします。これにより、ストリーミングパスは、ドキュメントに記載された 64 MB のピーク値を下回ります。1500 ms の実時間と 64 MB のピーク値という performance_budget の大部分は、暗号化ステップではなくドキュメントのレンダリングが占めます。

脅威モデルは明確です。アルゴリズムのダウングレードは、暗号ポリシーゲートによって緩和されます。このゲートは、どの処理の前にも、脆弱な暗号、脆弱なハッシュ、短い鍵を拒否します。エンジンは、要求されたプリミティブが存在しない場合に、より弱いプリミティブを暗黙的に代替することはありません。例外を送出し、オペレーターが対応できるようにします。ロギングを通じた鍵の開示は、KeyMaterial によって緩和されます。その文字列表現およびデバッグ表現は、バイト列を秘匿し、非可逆のフィンガープリントのみを公開します。暗号文の改ざんは、オプトインの AES-256-GCM パスでのみ検出されます。このパスでは認証タグが検証され、不一致の場合は平文を返すのではなく例外を送出します。デフォルトの AES-256-CBC パスは機密性のみを提供し、それ自体では変更を検出しません。GCM パスでの IV の再利用は、単調増加カウンターと多層防御の衝突セットによって緩和されます。これは NIST SP 800-38D §8 の一意 IV 要件に準拠しています。

境界も同様に明確です。AES-256 暗号化は、ISO 32000-2:2020 §7.6 で定義されているとおりに適用されます。実効的な保護は、パスワードの強度、鍵管理、デプロイメント環境、読み込み側のリーダーに依存します。パーミッションフラグは準拠リーダーによって尊重されますが、暗号的に強制されるものではありません。FIPS モードのプローブは、ホストの OpenSSL ビルドが FIPS プロバイダーをロードしたかどうかを報告します。このライブラリは、ホストが検証済みモジュールを提供する場合に FIPS 互換モードで動作しますが、ライブラリ自体がモジュールを認証することはありません。NIST SP 800-57 Part 1 §4 は、鍵のライフタイムと暗号化期間をデプロイメントの責任として位置づけています。Core はコントロールを公開しますが、ローテーションポリシーはデプロイメントが設定します。

暗号化サーフェスは、ドキュメントのバイト列をホスト外に送信しません。鍵導出、暗号化、復号は、プロセス内で実行されます。オプトインの GCM パスでは、メモリ内の IV 衝突セットを、鍵のバイト列ではなく非可逆の鍵フィンガープリントでキー付けします。セキュリティモジュールによって、パスワードや鍵の値がディスクに書き込まれることはありません。外部の鍵管理サービスを通じて鍵をルーティングするデプロイメントは、そのサービスのレジデンシーについて責任を負います。

KeyMaterial::__toString()__debugInfo() は秘匿されたプレースホルダーを返すため、鍵オブジェクトを誤ってログに記録しても、鍵のバイト列ではなくフィンガープリントが出力されます。setEncryption() に渡されるパスワードには #[\SensitiveParameter] 属性が付与されており、スタックトレースから秘匿されます。暗号処理の監査に適した識別子は、CryptoPolicyInterface::getName() によるポリシー名と 8 文字の鍵フィンガープリントです。これらをログに記録し、鍵やパスワードは決して記録しないでください。

脅威Core での緩和策残存する境界
アルゴリズムのダウングレード / 脆弱な暗号への置き換え暗号ポリシーゲート。暗黙的な弱体化なし(UnsupportedAlgorithmException を送出)ポリシーが注入された場合にのみ有効
ログを通じた鍵の開示KeyMaterial による秘匿。#[\SensitiveParameter] をパスワードに付与呼び出し側が exposeKey() をロガーに渡すと無効化
暗号文の改ざんオプトインパスでの GCM タグ検証デフォルトの CBC パスは機密性のみ
IV の再利用(GCM)単調増加カウンターと衝突セット。ロールオーバー時は処理を拒否
パーミッションフラグのバイパスなし — フラグは勧告的非準拠リーダーはフラグを無視する
脆弱なパスワードに対する総当たり攻撃SASLprep と反復鍵導出によりコストが上昇する脆弱なパスワードが依然として最大のリスク

Core は、FIPS 検証済みの暗号モジュールではなく、FIPS 認証も受けていません。CryptoCapabilities::detectFipsMode() はベストエフォートのランタイムプローブです。まずオペレーターのオーバーライドを確認し、次に OpenSSL プロバイダーリスト、続いてレガシーの FIPS モード呼び出しを確認して、有効、無効、不確定のいずれかを報告します。assertFipsAvailableForProfile() は、FIPS プロバイダーを証明できないホスト上で FIPS プロファイルが選択された場合、フェイルクローズします。このライブラリは、FIPS 検証済みプロバイダーをロードしたホストの OpenSSL ビルドに対して構成されている場合、FIPS 互換モードで動作します。検証および認証済みの FIPS 体制は Enterprise の領域です。Enterprise のドキュメントを参照してください。

主張規格条項エビデンス
GCM パスは、呼び出しごとにすべての IV を一意に保ち、規格の一意性要件に準拠します。NIST SP 800-38D§8.2.1
鍵のライフタイムと暗号化期間はデプロイメントの責任であり、Core はそのためのコントロールを公開します。NIST SP 800-57 パート 1 改訂 5§4
AES ファイル鍵は 256 ビットで、規格の鍵長と一致します。FIPS 197§4.2.1
トークン常駐の鍵生成は、外部鍵ストアの統合ポイントです。OASIS PKCS#11 v3.1C_GenerateKey

ISO 32000-2:2020 §7.6 は、標準セキュリティハンドラの規範的根拠です。その本文はライセンスで制限されているため、ここでは引用せずに言い換えており、条項は番号で参照しています。上記の各項目は、引用した規格から言い換えたものです。

Core は、暗号ポリシー契約を定義して固定し、AES-256 暗号化パスとローカル鍵サーフェスを提供します。Enterprise エディションは、同じ CryptoPolicyInterface の背後で、HSM/PKCS#11 の鍵保管パスと FIPS モードの暗号ポリシープロファイルを提供します。契約サーフェスはエディション間で同一です。違いは、デプロイメントが注入するポリシー実装と鍵保管バックエンドにあります。