Ir al contenido

Observabilidad: registro SIEM encadenado por hash e informes de renderizado

El módulo Observability contiene la implementación del estado en tiempo de ejecución: un registro de eventos SIEM encadenado por hash y a prueba de manipulaciones, la agregación de informes de render y de pilot, un registro de auditoría HSM y un conjunto completo de implementaciones no-op de métricas y trazas, de modo que la instrumentación pueda invocarse siempre.

Una sola página canónica por asunto. Los contratos de observabilidad — ContextAwareExceptionInterface, SpectrumInterface, JobNotificationInterface y el enum DegradationPolicy — están documentados en Contracts / Observability. Esta página documenta la implementación concreta del estado en tiempo de ejecución. Ambas páginas se complementan y no se duplican: la página de contratos cubre la SPI; esta página cubre el registro SIEM, los informes y las superficies de auditoría.

Ventana de terminal
composer require nextpdf/core:^3

En este módulo, el estado en tiempo de ejecución del motor se convierte en una salida duradera y verificable.

HashChainSiemEventLog es la superficie de nivel de seguridad. Implementa el contrato SiemEventEmitter y escribe un registro JSON-Lines en el que el hash de cada entrada es SHA-256(prev_hash_bytes || canonical_event_bytes). Esa cadena lineal de hashes hace que el registro sea a prueba de manipulaciones: alterar cualquier byte, eliminar una línea o reordenar líneas rompe la cadena. verifyIntegrity() recorre el archivo y devuelve el índice de la primera entrada inconsistente, o null si la cadena está intacta. readAll() transmite las entradas en flujo. Un flock(LOCK_EX) consultivo por proceso delimita la sección crítica de leer-la-cola-y-luego-anexar, de modo que procesos PHP concurrentes sobre el mismo archivo no entrelacen entradas. El diseño es honesto sobre su límite: es una cadena lineal de hashes, no un árbol de Merkle RFC 6962 — suficiente para evidenciar manipulaciones, pero no para pruebas de inclusión eficientes. El código fuente así lo afirma. SiemEvent transporta el evento tipado con toCanonicalJson(). SiemEventSeverity y SiemEventType lo clasifican. CorrelationContext y CorrelationIdGenerator enhebran un id de correlación a través de eventos relacionados.

RenderReportBuilder, RenderReport, PilotReportAggregator y PilotSummary conforman la superficie de informes (@since 5.1.0). El agregador recopila objetos RenderReport y produce un PilotSummary que puede representarse como array, JSON o Markdown — el formato consumido por una revisión de operaciones.

HsmAuditLogInterface / HsmAuditEvent registran operaciones de firma respaldadas por HSM para la capa de seguridad. Las interfaces MetricsCounterInterface, MetricsGaugeInterface, MetricsHistogramInterface y TraceSpanInterface definen las formas de metrics/trace. Las implementaciones NoOp* proporcionan una reserva inerte completa para que el motor pueda emitir métricas y spans sin un backend configurado.

Estabilidad: experimental. El registro SIEM se etiqueta de forma interna por ciclo en lugar de llevar un @since semver congelado, y la superficie de informes es @since 5.1.0. Las superficies son funcionales y están probadas, pero la forma de la API puede evolucionar. Tratar el formato del registro (JSON canónico + cadena de hashes) como el contrato estable y la API de PHP como aún en consolidación.

ClaseMiembros claveFunción
HashChainSiemEventLogemit(SiemEvent), verifyIntegrity(): ?int, readAll(): GeneratorRegistro SIEM encadenado por hash y a prueba de manipulaciones
SiemEventtoCanonicalJson()Evento SIEM tipado
SiemEventSeverity / SiemEventType (enums)clasificaciónSeveridad y tipo del evento
CorrelationContext / CorrelationIdGeneratorpropagación de correlaciónCorrelaciona eventos relacionados
RenderReportBuilder / RenderReportensamblaje del informeInforme por renderizado (@since 5.1.0)
PilotReportAggregatoraddReport(), count(), getSummary(), toJson(), toMarkdown(), exportReportsJson()Agrega informes de renderizado (@since 5.1.0)
PilotSummarytoArray(), toJson(), toMarkdown()Resumen de revisión de operaciones (@since 5.1.0)
HsmAuditLogInterface / HsmAuditEventRegistro de auditoría HSMRegistro de auditoría de operaciones HSM
NoOpSiemEventEmitter, NoOpMetricsCounter, NoOpTraceSpan, …reservas inertesImplementaciones no-op completas

Ejecutar composer docs:generate-api-php -- --module=Observability para obtener la tabla PHPDoc completa.

Emitir un evento y verificar la integridad del registro.

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

Envolver el emisor para que un fallo de registro en la ruta crítica de firma sea una decisión local, no una excepción no capturada.

<?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() lanza SiemEmitterException ante un error de escritura. El código llamador en la ruta crítica de firma debe envolver la llamada y decidir localmente si ignora el error, reintenta o aborta. El emisor no toma esa decisión.
  • verifyIntegrity() devuelve el índice de la primera entrada rota, o null. Un resultado no nulo significa que el registro está comprometido a partir de ese punto — no confiar en las entradas en esa posición ni posteriores.
  • El flock consultivo es por proceso y sobre el mismo archivo. La concurrencia entre hosts requiere un sumidero fuera de banda (reenviar a syslog). No asumir que el bloqueo de archivo coordina entre máquinas.
  • Esto es una cadena lineal de hashes, no un árbol de Merkle. Aporta evidencia de manipulación, no pruebas de inclusión eficientes. No presentarlo como lo segundo.
  • Las reservas NoOp* son completas e inertes. No bifurcar según la disponibilidad del backend para «ahorrar trabajo» — el no-op ya no cuesta nada.

emit() lee el hash de la entrada anterior y anexa una línea con un bloqueo de archivo activo — O(1) por evento más el bloqueo. verifyIntegrity() es O(n) en el número de entradas porque recorre toda la cadena. Ejecutarlo de forma programada, no en la ruta crítica. La agregación de informes es lineal en el número de informes. El perfil de reproducibilidad es structural: los eventos y los informes llevan marcas de tiempo e ids de correlación, de modo que dos ejecuciones difieren en esos campos mientras la estructura de la cadena es determinista.

El registro SIEM es un control de seguridad. Su evidencia de manipulación depende de que el archivo de registro y el paso de verificación estén protegidos: almacenar el archivo en un almacenamiento apto para anexar y con control de acceso, ejecutar verifyIntegrity() de forma programada y reenviar las entradas fuera de banda para que el compromiso de un host no pueda reescribir el historial en silencio. Los eventos pueden transportar contexto sensible. Aplicar la obligación de depuración de registros del proyecto antes de construir el evento, no después de encadenarlo, porque una reescritura depurada rompería la cadena. El registro de auditoría HSM registra operaciones de firma y también es relevante para la seguridad. Tratarlo con las mismas protecciones. Consultar el modelo de amenazas del motor en /modules/core/security/.

Este módulo no declara ningún requisito normativo de la especificación PDF. Implementa mecanismos de integridad de registros y de observabilidad cuyo diseño se alinea con las prácticas de gestión de registros y verificación de integridad de NIST SP 800-92 — una alineación con un marco de control documentada en el código fuente, no una cita PDF anclada a un fragmento. La conformidad de los documentos que produce el motor se valida mediante el oráculo y las suites golden descritas en /modules/core/conformance/.