オブザーバビリティ: ハッシュチェーン化 SIEM ログとレンダーレポート
オブザーバビリティモジュールは、ランタイム状態を担う 実装 です。改ざん検知可能なハッシュチェーン化 SIEM イベントログ、レンダーおよびパイロットレポートの集約、HSM 監査ログ、そして計装を常に呼び出せるようにする no-op の metrics と trace 実装一式を提供します。
関心事ごとの正規ページは 1 つ。 オブザーバビリティの contracts —
ContextAwareExceptionInterface、SpectrumInterface、JobNotificationInterface、およびDegradationPolicyenum — は Contracts / Observability で文書化されています。このページでは、具体的なランタイム状態の実装を文書化します。この 2 つは重複ではなく相補的です。SPI については contracts ページを、SIEM ログ・レポート・監査の各サーフェスについてはこのページを参照してください。
インストール
「インストール」という見出しのセクションcomposer require nextpdf/core:^3概念的な全体像
「概念的な全体像」という見出しのセクションこのモジュールは、エンジンのランタイム状態が永続的で検証可能な出力として残される場所です。
HashChainSiemEventLog はセキュリティグレードのサーフェスです。これは SiemEventEmitter コントラクトを実装し、各レコードのハッシュが SHA-256(prev_hash_bytes || canonical_event_bytes) となる JSON-Lines ログを書き込みます。この線形ハッシュチェーンにより、ログは改ざん検知可能になります。どのバイトであっても、変更、行の削除、行の並べ替えはいずれもチェーンを壊します。verifyIntegrity() はファイルを走査し、最初に不整合となったレコードのインデックスを返します。チェーンが無傷であれば null を返します。readAll() はレコードをストリーミングで読み出します。プロセスごとのアドバイザリな flock(LOCK_EX) が「末尾読み取りから追記まで」のクリティカルセクションを囲むため、同一ファイルに対する複数の PHP プロセスがレコードを混在させることはありません。設計上の境界は明確です。これは線形ハッシュチェーンであり、RFC 6962 の Merkle ツリーではありません。改ざん検知には十分ですが、効率的な包含証明には向きません。これはソース自体にも明記されています。SiemEvent は型付きのイベントを toCanonicalJson() とともに保持します。SiemEventSeverity と SiemEventType がそれを分類します。CorrelationContext と CorrelationIdGenerator が、関連するイベント全体に相関 ID を通します。
RenderReportBuilder、RenderReport、PilotReportAggregator、および PilotSummary はレポートのサーフェスです(@since 5.1.0)。アグリゲーターは RenderReport を収集し、配列、JSON、Markdown にレンダリングできる PilotSummary を生成します。これは運用レビューで利用される形式です。
HsmAuditLogInterface / HsmAuditEvent は、セキュリティ層向けに HSM ベースの署名操作を記録します。MetricsCounterInterface、MetricsGaugeInterface、MetricsHistogramInterface、および TraceSpanInterface が metrics/trace の形状を定義します。NoOp* 実装は完全な不活性フォールバックを提供するため、エンジンはバックエンドを構成しなくても metrics とスパンを発行できます。
安定性: 実験的。 SIEM ログは凍結された semver
@sinceを持つのではなく、内部的にサイクルタグで管理されており、レポートのサーフェスは@since 5.1.0です。これらのサーフェスは機能しており、テスト済みですが、API の形状は変化する可能性があります。ログの フォーマット(正規 JSON + ハッシュチェーン)を安定したコントラクトとして扱い、PHP API はまだ固まりきっていないものとして扱ってください。
API サーフェス
「API サーフェス」という見出しのセクション| クラス | 主なメンバー | 役割 |
|---|---|---|
HashChainSiemEventLog | emit(SiemEvent), verifyIntegrity(): ?int, readAll(): Generator | 改ざん検知可能なハッシュチェーン化 SIEM ログ |
SiemEvent | toCanonicalJson() | 型付き SIEM イベント |
SiemEventSeverity / SiemEventType(enum) | 分類 | イベントの重大度と種別 |
CorrelationContext / CorrelationIdGenerator | 相関のスレッディング | 関連イベントの相関付け |
RenderReportBuilder / RenderReport | レポートの組み立て | レンダーごとのレポート(@since 5.1.0) |
PilotReportAggregator | addReport(), count(), getSummary(), toJson(), toMarkdown(), exportReportsJson() | レンダーレポートを集約(@since 5.1.0) |
PilotSummary | toArray(), toJson(), toMarkdown() | 運用レビュー用サマリー(@since 5.1.0) |
HsmAuditLogInterface / HsmAuditEvent | HSM 監査レコード | HSM 操作の監査ログ |
NoOpSiemEventEmitter, NoOpMetricsCounter, NoOpTraceSpan, … | 不活性フォールバック | 完全な no-op 実装 |
完全な PHPDoc テーブルを確認するには、composer docs:generate-api-php -- --module=Observability を実行してください。
コードサンプル — クイックスタート
「コードサンプル — クイックスタート」という見出しのセクションイベントを発行し、ログの整合性を検証します。
<?php
declare(strict_types=1);
require_once __DIR__ . '/../vendor/autoload.php';
use NextPDF\Observability\Siem\HashChainSiemEventLog;use NextPDF\Observability\Siem\SiemEvent;
$log = new HashChainSiemEventLog('/var/log/nextpdf/siem.jsonl');$log->emit(new SiemEvent(/* type, severity, payload */));
$firstBroken = $log->verifyIntegrity();echo $firstBroken === null ? "SIEM chain intact.\n" : "Tamper detected at record {$firstBroken}.\n";コードサンプル — 本番
「コードサンプル — 本番」という見出しのセクション署名のホットパスでロギングに失敗しても、キャッチされない例外ではなくローカルな判断として扱えるように、エミッターをラップします。
<?php
declare(strict_types=1);
require_once __DIR__ . '/../vendor/autoload.php';
use NextPDF\Observability\Siem\HashChainSiemEventLog;use NextPDF\Observability\Siem\Exception\SiemEmitterException;use NextPDF\Observability\Siem\SiemEvent;use Psr\Log\LoggerInterface;
final readonly class AuditedSiemSink{ public function __construct( private HashChainSiemEventLog $log, private LoggerInterface $fallback, ) {}
public function record(SiemEvent $event): void { try { $this->log->emit($event); } catch (SiemEmitterException $e) { // Do not let SIEM I/O abort the signing path; record and continue. $this->fallback->critical('SIEM emit failed; event not chained.', [ 'error' => $e->getMessage(), ]); } }}エッジケースと落とし穴
「エッジケースと落とし穴」という見出しのセクションemit()は書き込みエラー時にSiemEmitterExceptionをスローします。署名のホットパスにいる呼び出し側はそれをラップし、握りつぶすか、再試行するか、中断するかをローカルに判断しなければなりません。エミッターが代わりに判断することはありません。verifyIntegrity()は 最初に 壊れたレコードのインデックスを返すか、nullを返します。null でない結果は、その地点からログが侵害されていることを意味します。その位置以降のレコードは信頼しないでください。- アドバイザリな
flockはプロセスごと・同一ファイルに対するものです。ホストをまたぐ並行処理には、帯域外のシンク(syslog への転送)が必要です。ファイルロックがマシンをまたいで協調すると想定しないでください。 - これは線形ハッシュチェーンであり、Merkle ツリーではありません。改ざん検知を証明しますが、効率的な包含証明は提供しません。後者の機能があるものとして説明しないでください。
NoOp*フォールバックは完全かつ不活性です。「処理を節約する」ためにバックエンドの可用性で分岐しないでください。no-op はすでにコストがかかりません。
パフォーマンス
「パフォーマンス」という見出しのセクションemit() は前のレコードのハッシュを読み取り、ファイルロックの下で 1 行を追記します。イベントごとのコストは O(1) とロックです。verifyIntegrity() はチェーン全体を走査するため、レコード数に対して O(n) です。ホットパス上ではなく、スケジュールに従って実行してください。レポートの集約はレポート数に対して線形です。再現性のプロファイルは structural です。イベントとレポートはタイムスタンプと相関 ID を持つため、2 回の実行ではそれらのフィールドが異なる一方、チェーン構造は決定的です。
セキュリティに関する注意
「セキュリティに関する注意」という見出しのセクションSIEM ログはセキュリティコントロールです。その改ざん検知性は、ログファイルと検証ステップが保護されていることに依存します。ファイルは追記に適したアクセス制御付きストレージに保存し、verifyIntegrity() をスケジュールに従って実行し、ホスト侵害によって履歴が密かに書き換えられないようレコードを帯域外へ転送してください。イベントは機微なコンテキストを含む場合があります。プロジェクトのログスクラビング義務は、イベントがチェーンに組み込まれた後ではなく、構築される前に適用してください。後から書き換えるとチェーンを壊すためです。HSM 監査ログは署名操作を記録し、それ自体がセキュリティ上重要です。これにも同じ保護を適用してください。エンジンの脅威モデルについては /modules/core/security/ を参照してください。
このモジュールは、PDF 仕様について規範的な主張を一切行いません。これはログ整合性とオブザーバビリティの仕組みを実装しており、その設計は NIST SP 800-92 のログ管理および整合性検証のプラクティスと整合しています。これはソースに文書化されたコントロールフレームワークとの整合であり、チャンク固定の PDF 引用ではありません。エンジンが 生成する ドキュメントの準拠性は、/modules/core/conformance/ に記載されたオラクルおよびゴールデンスイートによって検証されます。
- Contracts / Observability — SPI(構造化された例外、Spectrum、デグレードポリシー)。
- Telemetry モジュール — 外部バックエンドへ供給する OpenTelemetry ブリッジ。
- Audit モジュール — SIEM ログと対になるコンプライアンス証跡のエクスポーター。
- Security モジュール — HSM 監査ログが記録する署名操作。
- 準拠性の概要