ข้ามไปยังเนื้อหา

Observability: SIEM log แบบ hash-chain และรายงานการเรนเดอร์

โมดูล Observability มี การนำไปใช้งาน สำหรับสถานะรันไทม์ ได้แก่ event log ของ security information and event management (SIEM) แบบ hash-chain ที่ตรวจพบการดัดแปลงได้ การรวมรายงาน render และ pilot, log การตรวจสอบของ hardware security module (HSM) รวมถึงการนำ metrics และ trace แบบ no-op ไปใช้งานครบถ้วน เพื่อให้เรียกใช้ instrumentation ได้เสมอ

หนึ่งหน้าตามหลักการสำหรับแต่ละประเด็น contracts ของ observability — ContextAwareExceptionInterface, SpectrumInterface, JobNotificationInterface และ enum DegradationPolicy — มีเอกสารอธิบายไว้ ที่ Contracts / Observability หน้านี้ อธิบายการนำสถานะรันไทม์ไปใช้จริง ทั้งสองหน้า เสริมกัน ไม่ได้ซ้ำซ้อนกัน ใช้หน้า contracts สำหรับ service provider interface (SPI) และใช้หน้านี้สำหรับ SIEM log การรายงาน และ audit แทน

Terminal window
composer require nextpdf/core:^3

โมดูลนี้แปลงสถานะรันไทม์ของเอนจินให้เป็นเอาต์พุตที่คงทนและตรวจสอบได้

HashChainSiemEventLog คือพื้นผิวระดับความปลอดภัย โดยนำ contract SiemEventEmitter ไปใช้งานและเขียน log แบบ JavaScript Object Notation (JSON) Lines โดยที่ hash ของแต่ละเรกคอร์ดคือ SHA-256(prev_hash_bytes || canonical_event_bytes). hash-chain เชิงเส้นนี้ ทำให้ log ตรวจพบการดัดแปลงได้ หาก byte ใดเปลี่ยนไป มีบรรทัดถูกลบ หรือมีการสลับลำดับบรรทัด chain จะขาด verifyIntegrity() จะไล่อ่านไฟล์และคืนค่า index ของเรกคอร์ดแรกที่ไม่สอดคล้องกัน หรือ null เมื่อ chain ยังสมบูรณ์ readAll() จะสตรีมเรกคอร์ดออกมา flock(LOCK_EX) แบบ advisory ต่อโปรเซสจะปกป้อง critical section ของการอ่านส่วนท้ายแล้วต่อท้าย ดังนั้นโปรเซส PHP ที่ทำงานพร้อมกันบนไฟล์เดียวกันจะไม่เขียนเรกคอร์ดสลับกัน ขอบเขตนี้ระบุไว้อย่างชัดเจน นี่คือ hash-chain เชิงเส้น ไม่ใช่ Merkle tree ตาม Request for Comments (RFC) 6962 จึงเพียงพอสำหรับการตรวจพบการดัดแปลง แต่ไม่ใช่สำหรับ inclusion proof ที่มีประสิทธิภาพ ซอร์สโค้ดระบุไว้เช่นนั้น SiemEvent จะบรรจุ event ที่มีชนิดกำกับพร้อมด้วย toCanonicalJson() SiemEventSeverity และ SiemEventType จะจำแนกประเภทของ event CorrelationContext และ CorrelationIdGenerator จะส่งต่อ correlation id ระหว่าง event ที่เกี่ยวข้องกัน

RenderReportBuilder, RenderReport, PilotReportAggregator และ PilotSummary ประกอบกันเป็นพื้นผิวการรายงาน (@since 5.1.0) aggregator จะรวบรวม RenderReport หลายรายการและสร้าง PilotSummary ที่แสดงผลเป็น array, JSON หรือ Markdown ในรูปแบบที่การตรวจสอบด้านปฏิบัติการนำไปใช้ได้

HsmAuditLogInterface / HsmAuditEvent จะบันทึกการดำเนินการลงนามที่ใช้ HSM สำหรับเลเยอร์ความปลอดภัย MetricsCounterInterface, MetricsGaugeInterface, MetricsHistogramInterface และ TraceSpanInterface เป็นตัวกำหนดรูปแบบของ metrics และ trace การนำ NoOp* ไปใช้งานให้ fallback ที่ไม่ทำงานครบถ้วน ดังนั้นเอนจินจึงส่งออก metrics และ span ได้โดยไม่ต้องมี backend ที่กำหนดค่าไว้

ความเสถียร: experimental SIEM log ใช้การกำกับ cycle ภายในแทน การระบุ @since ของ Semantic Versioning (semver) แบบตายตัว และ พื้นผิวการรายงานเป็น @since 5.1.0 พื้นผิวเหล่านี้ใช้งานได้และผ่านการทดสอบแล้ว แต่รูปแบบของ application programming interface (API) อาจเปลี่ยนแปลงได้ ให้ถือว่า รูปแบบ ของ log (canonical JSON + hash-chain) เป็น contract ที่เสถียร และให้ถือว่า API ของ PHP ยังไม่นิ่ง

คลาสสมาชิกหลักบทบาท
HashChainSiemEventLogemit(SiemEvent), verifyIntegrity(): ?int, readAll(): GeneratorSIEM log แบบ hash-chain ที่ตรวจพบการดัดแปลงได้
SiemEventtoCanonicalJson()SIEM event ที่มีชนิดกำกับ
SiemEventSeverity / SiemEventType (enum)การจำแนกประเภทจำแนกระดับความรุนแรงและประเภทของ event
CorrelationContext / CorrelationIdGeneratorการร้อยเรียง correlationร้อยเรียง correlation ผ่าน event ที่เกี่ยวข้องกัน
RenderReportBuilder / RenderReportการประกอบรายงานสร้างรายงานต่อการเรนเดอร์แต่ละครั้ง (@since 5.1.0)
PilotReportAggregatoraddReport(), count(), getSummary(), toJson(), toMarkdown(), exportReportsJson()รวมรายงานการเรนเดอร์ (@since 5.1.0)
PilotSummarytoArray(), toJson(), toMarkdown()สรุปเอาต์พุตของการตรวจสอบด้านปฏิบัติการ (@since 5.1.0)
HsmAuditLogInterface / HsmAuditEventเรกคอร์ดการตรวจสอบ HSMบันทึกการตรวจสอบการดำเนินการของ HSM
NoOpSiemEventEmitter, NoOpMetricsCounter, NoOpTraceSpan, …fallback ที่ไม่ทำงานให้การนำ no-op ไปใช้งานครบถ้วน

เรียกใช้ composer docs:generate-api-php -- --module=Observability เพื่อสร้างตาราง PHPDoc ฉบับเต็ม

ส่ง event และตรวจสอบความสมบูรณ์ของ log

<?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";

ห่อหุ้ม emitter ไว้ เพื่อให้ความล้มเหลวในการบันทึก log บน hot path ของการลงนามยังเป็นการตัดสินใจระดับ local แทนที่จะกลายเป็น exception ที่ไม่ถูกจับ

<?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 เมื่อเกิดข้อผิดพลาดในการเขียน ผู้เรียกบน hot path ของการลงนามต้องห่อหุ้มและตัดสินใจในระดับ local ว่าจะกลืน exception ลองใหม่ หรือยกเลิก emitter จะไม่ตัดสินใจแทนคุณ
  • verifyIntegrity() จะคืนค่า index ของเรกคอร์ดที่เสียหาย รายการแรก หรือ null ผลลัพธ์ที่ไม่ใช่ null หมายความว่า log ถูกบุกรุกตั้งแต่จุดนั้นเป็นต้นไป อย่าเชื่อถือเรกคอร์ดที่จุดนั้นหรือหลังจากนั้น
  • การล็อกแบบ advisory ด้วย flock ทำงานต่อโปรเซสและภายในไฟล์เดียวกัน การทำงานพร้อมกันข้าม host ต้องใช้ sink แบบ out-of-band เช่น การส่งต่อ syslog อย่าสันนิษฐานว่า file lock ทำหน้าที่ประสานงานข้ามเครื่องได้
  • นี่คือ hash-chain เชิงเส้น ไม่ใช่ Merkle tree ให้การตรวจพบการดัดแปลง ไม่ใช่ inclusion proof ที่มีประสิทธิภาพ อย่านำไปสื่อสารทางการตลาดในฐานะอย่างหลัง
  • fallback ของ NoOp* ครบถ้วนและไม่ทำงาน อย่าเขียนเงื่อนไขแยกตามความพร้อมใช้งานของ backend เพื่อ “ประหยัดงาน” no-op ไม่มีต้นทุนอยู่แล้ว

emit() จะอ่าน hash ของเรกคอร์ดก่อนหน้าและต่อท้ายหนึ่งบรรทัดภายใต้ file lock มีค่า O(1) ต่อ event บวกกับ lock verifyIntegrity() มีค่า O(n) ตามจำนวนเรกคอร์ด เนื่องจากต้องไล่อ่าน chain ทั้งหมด ให้เรียกใช้ตามกำหนดเวลา ไม่ใช่บน hot path การรวมรายงานเป็นเชิงเส้นตามจำนวนรายงาน โปรไฟล์ความสามารถในการทำซ้ำเป็นแบบ structural event และรายงานบรรจุ timestamp และ correlation id ดังนั้นการรันสองครั้งจะแตกต่างกันในฟิลด์เหล่านั้น ขณะที่โครงสร้างของ chain ยังคง deterministic

SIEM log เป็นมาตรการควบคุมด้านความปลอดภัย การตรวจพบการดัดแปลงขึ้นอยู่กับการปกป้องทั้งไฟล์ log และขั้นตอนการตรวจสอบ จัดเก็บไฟล์บน storage ที่เหมาะกับการต่อท้ายและมีการควบคุมการเข้าถึง เรียกใช้ verifyIntegrity() ตามกำหนดเวลา และส่งต่อเรกคอร์ดแบบ out-of-band เพื่อให้การบุกรุก host ไม่สามารถเขียนประวัติใหม่อย่างเงียบ ๆ ได้ event อาจบรรจุบริบทที่ละเอียดอ่อน ดำเนินการตามข้อกำหนดการขัดล้าง log ของโปรเจกต์ก่อนสร้าง event ไม่ใช่หลังจากนำเข้า chain แล้ว เนื่องจากการเขียนใหม่ที่ขัดล้างแล้วจะทำให้ chain ขาด log การตรวจสอบของ HSM จะบันทึกการดำเนินการลงนามและตัว log เองก็เกี่ยวข้องกับความปลอดภัย ให้ปกป้อง log นี้ด้วยมาตรการเดียวกัน ดู threat model ของเอนจินได้ที่ /modules/core/security/ ในส่วนนี้

โมดูลนี้ไม่ได้อ้างสิทธิ์เชิงบรรทัดฐานใด ๆ เกี่ยวกับข้อกำหนด PDF โมดูลนี้นำกลไกความสมบูรณ์ของ log และ observability ไปใช้งาน โดยมีการออกแบบที่สอดคล้องกับแนวปฏิบัติด้านการจัดการ log และการตรวจสอบความสมบูรณ์ใน NIST SP 800-92 ความสอดคล้องกับ control-framework ดังกล่าวมีเอกสารระบุไว้ในซอร์สโค้ด ไม่ใช่การอ้างอิง PDF แบบตรึง chunk ความสอดคล้องของเอกสารที่เอนจิน ผลิต ออกมาจะได้รับการตรวจสอบโดยชุด oracle และ golden ที่อธิบายไว้ใน /modules/core/conformance/ แทน