Eine vorhandene Signatur inspizieren und die Vertrauensgrenze verstehen
Auf einen Blick
Abschnitt betitelt „Auf einen Blick“Dieses Recipe verwendet den Core-Inspector, um zu erkennen, ob ein PDF ein Signatur-Dictionary enthält. Der Inspector läuft offline und verwendet den Spectrum-Sidecar nicht. Das Recipe zieht außerdem eine klare Grenze: Das Erkennen einer Signatur ist nicht gleichbedeutend mit ihrer Prüfung. Kryptografische Prüfung, Validierung des Vertrauenspfads und Sperrprüfung sind Premium-Funktionalität oder extern zu leisten.
Installation
Abschnitt betitelt „Installation“composer require nextpdf/core:^3Konzeptioneller Überblick
Abschnitt betitelt „Konzeptioneller Überblick“Eine Signatur in einem PDF ist ein Signaturfeld, dessen Wert ein Signatur-Dictionary ist (ISO 32000-2 §12.7.4). Der Contents-Eintrag des Dictionaries enthält DER-kodierte CMS-SignedData (ISO 32000-2 §12.8.1). Der Quick-Fallback des Inspector erkennt das Vorhandensein einer solchen Struktur, indem er nach den Signatur-Markern sucht. Er parst die CMS nicht, berechnet weder den Byte-Range-Digest neu (der den Signaturwert ausschließt — ISO 32000-2 §12.8.1) noch validiert er die Zertifikatskette oder prüft den Sperrstatus.
API-Oberfläche
Abschnitt betitelt „API-Oberfläche“Rufen Sie new Inspector() auf, anschließend ->inspect(string $pdfData, InspectConfig $config). Verwenden Sie InspectConfig::quick() für den Offline-PHP-Fallback. InspectDepth::Standard/Full benötigen den Spectrum-Sidecar und schlagen abgesichert fehl (INSPECT-SIDECAR-001), wenn er fehlt. Das Ergebnis ist ein InspectResult-Wertobjekt. Die hier relevanten Felder sind $hasSigned (Vorhandensein einer Signatur), $isEncrypted und $pdfVersion.
Codebeispiel — Schnellstart
Abschnitt betitelt „Codebeispiel — Schnellstart“<?php
declare(strict_types=1);
require_once __DIR__ . '/vendor/autoload.php';
use NextPDF\Inspect\InspectConfig;use NextPDF\Inspect\Inspector;
$pdfData = file_get_contents(__DIR__ . '/incoming.pdf');if ($pdfData === false || $pdfData === '') { fwrite(STDERR, "Cannot read incoming.pdf\n"); exit(1);}
$result = (new Inspector())->inspect($pdfData, InspectConfig::quick());
// hasSigned reports the PRESENCE of a signature dictionary.// It does NOT mean the signature verifies.echo $result->hasSigned ? "A signature is present — NOT verified.\n" : "No signature found.\n";Codebeispiel — Produktion
Abschnitt betitelt „Codebeispiel — Produktion“Dies ist ein eigenständiges, im Harness ausführbares Programm. Es spiegelt examples/37-inspect-existing-signature.php wider. Das Programm inspiziert ein bekannt signiertes Korpus-Sample und ein frisch erzeugtes unsigniertes Dokument, sodass beide Zweige des Vorhandensein-Flags beobachtbar sind. Anschließend gibt es das Verdikt weiter. Das Vorhandensein dient als Routing-Eingabe, niemals als Vertrauensverdikt. Die Datei wird an einen kryptografischen Verifier (Pro oder extern) übergeben. Hier wird ihr nicht vertraut.
<?php
declare(strict_types=1);
require_once __DIR__ . '/vendor/autoload.php';
use NextPDF\Core\Document;use NextPDF\Inspect\InspectConfig;use NextPDF\Inspect\Inspector;
$inspector = new Inspector();
// --- A known-signed input ---// The repository corpus carries synthetic PAdES samples. In your// application this is simply the incoming PDF you received.$signedPath = __DIR__ . '/tests/Corpus/pades/pades-b-b-bytepattern-synthetic.pdf';if (is_file($signedPath)) { $signed = (string) file_get_contents($signedPath); $r = $inspector->inspect($signed, InspectConfig::quick());
echo "Signed sample:\n"; echo ' Signature present : ' . ($r->hasSigned ? 'yes' : 'no') . "\n"; echo ' Encrypted : ' . ($r->isEncrypted ? 'yes' : 'no') . "\n"; echo ' PDF version : ' . ($r->pdfVersion ?? 'unknown') . "\n"; echo " Verdict : presence detected — NOT verified.\n";
if ($r->hasSigned) { // Presence detected. This is routing input, not a trust verdict. // Hand the file to a cryptographic verifier (Pro or external) // before relying on it. (Pseudo-queue shown; wire your own.) // $verifierQueue->enqueue($signed); echo " Next step : run a cryptographic verifier before trusting it.\n"; }} else { echo "Signed corpus sample absent; skipping the signed branch.\n";}
// --- A known-unsigned input ---$unsigned = Document::createStandalone();$unsigned->setTitle('Unsigned sample');$unsigned->addPage();$unsigned->setFont('helvetica', '', 12);$unsigned->cell(0, 10, 'This document carries no signature.', newLine: true);$unsignedBytes = $unsigned->getPdfData();
$ru = $inspector->inspect($unsignedBytes, InspectConfig::quick());echo "Unsigned sample:\n";echo ' Signature present : ' . ($ru->hasSigned ? 'yes' : 'no') . "\n";
// The harness sets NEXTPDF_COOKBOOK_OUTPUT and runs this script under the// semantic profile; emit the unsigned document to the side-channel.$out = getenv('NEXTPDF_COOKBOOK_OUTPUT');file_put_contents($out !== false && $out !== '' ? $out : __DIR__ . '/inspected.pdf', $unsignedBytes);Erwartete STDOUT-Ausgabe (der signierte Zweig wird übersprungen, wenn das Korpus-Sample fehlt):
Signed sample: Signature present : yes Encrypted : no PDF version : <version> Verdict : presence detected — NOT verified. Next step : run a cryptographic verifier before trusting it.Unsigned sample: Signature present : noRandfälle & Fallstricke
Abschnitt betitelt „Randfälle & Fallstricke“- Vorhandensein ist keine Gültigkeit.
$hasSignedmeldet, dass ein Signatur-Dictionary existiert. Es prüft weder die CMS-Struktur, den Byte-Range-Digest, das Signaturzertifikat, die Kette noch den Sperrstatus. Eine manipulierte Datei kann trotzdemhasSigned = truemelden. Behandeln Sie das Vorhandensein niemals als Nachweis von Integrität oder Urheberschaft. - Was für eine vollständige Prüfung erforderlich ist. Für eine vollständige Entscheidung wird der Byte-Range-Digest neu berechnet (ISO 32000-2 §12.8.1), die CMS-SignedData validiert, der X.509-Pfad zu einem vertrauenswürdigen Anker aufgebaut und geprüft sowie der Sperrstatus über OCSP oder CRL kontrolliert. Ein Signaturzeitstempel wird, sofern vorhanden, selbst gegen seinen eigenen Abdruck über die Oktette des Signaturwerts geprüft (ETSI EN 319 122-1 §5.3). Diese Operationen laufen hinter den Signing-Contracts ab. Produktive Implementierungen werden in Pro und Enterprise ausgeliefert. Ein externer Validator ist die andere unterstützte Option.
- Inspektionstiefe.
InspectConfig::quick()ist die einzige Tiefe, die ohne den Spectrum-Sidecar läuft.Standard/FullwerfenINSPECT-SIDECAR-001, wenn der Sidecar nicht verfügbar ist. - Leere Eingabe. Ein leerer String wirft eine Inspect-Exception mit „PDF data must not be empty“. Sichern Sie das Einlesen ab.
- Mehrere Signaturen / Zeitstempel. Das Vorhandensein-Flag zählt keine Signaturen und unterscheidet auch eine Freigabesignatur nicht von einem Dokumentzeitstempel (der gemäß RFC 5652 §5.3 ebenfalls in
unsignedAttrsgetragen wird). Verwenden Sie einen dedizierten Verifier, wenn die Anzahl oder das Verdikt pro Signatur wichtig ist.
Performance
Abschnitt betitelt „Performance“Der Quick-Fallback ist ein begrenzter Scan über die Dokumentbytes. Er parst nicht den vollständigen Objektgraphen. Er eignet sich für eine schnelle Triage eingehender Dateien, bevor Sie sie an einen aufwendigeren Verifier weiterleiten.
Sicherheitshinweise
Abschnitt betitelt „Sicherheitshinweise“Der Inspector ist ein Triage-Werkzeug, keine Vertrauensgrenze. Ein positives hasSigned darf niemals allein eine Vertrauensentscheidung steuern.
Datenresidenz & PII-Maßnahmen
Abschnitt betitelt „Datenresidenz & PII-Maßnahmen“Die Inspektion läuft vollständig im Prozess. Keine Dokumentbytes verlassen den Host. Der Quick-Fallback liest nur strukturelle Marker, keinen Dokumenttext, sodass keine PII extrahiert oder übertragen wird.
Sichere Telemetrie & Log-Bereinigung
Abschnitt betitelt „Sichere Telemetrie & Log-Bereinigung“Inspector akzeptiert einen optionalen PSR-3-Logger. Er protokolliert den gewählten Pfad („Spectrum unavailable, using PHP fallback“), nicht den Dokumentinhalt. Protokollieren Sie die inspizierten PDF-Bytes oder das InspectResult nicht wörtlich, wenn das Dokument sensibel ist.
Bedrohungsmodell
Abschnitt betitelt „Bedrohungsmodell“Berücksichtigt werden eine manipulierte Datei, die ein syntaktisch gültiges Signatur-Dictionary aufweist (der Inspector meldet das Vorhandensein; er behauptet ausdrücklich keine Integrität), und eine Datei ohne Signatur (korrekt als nicht vorhanden gemeldet). Nicht behauptet wird, dass eine erkannte Signatur kryptografisch gültig, vertrauenswürdig oder nicht gesperrt ist — das ist Aufgabe des Verifiers.
Verhalten im FIPS-Modus
Abschnitt betitelt „Verhalten im FIPS-Modus“Der Quick-Fallback führt keine Kryptografie aus, daher ist der FIPS-Modus für dieses Recipe nicht relevant. Bei der kryptografischen Prüfung (Premium/extern) kommt es auf die FIPS-Providerkette an.
Konformität
Abschnitt betitelt „Konformität“| Aussage | Spezifikation | Klausel | reference_id |
|---|---|---|---|
| Der Wert eines Signaturfelds ist ein Signatur-Dictionary. | ISO 32000-2 | §12.7.4 | |
Contents enthält DER-CMS-SignedData; bei einem Dokumentzeitstempel enthält Contents einen TimeStampToken. | ISO 32000-2 | §12.8.1 | |
| Die Prüfung berechnet den Digest über den Byte-Range neu und schließt dabei den Signaturwert aus. | ISO 32000-2 | §12.8.1 | |
| Der Abdruck eines Signaturzeitstempels erstreckt sich über die Oktette des Signaturwerts der SignerInfo. | ETSI EN 319 122-1 | §5.3 | |
| Ein Zeitstempel wird in den unsignedAttrs der SignerInfo getragen. | RFC 5652 | §5.3 |
Dieses Recipe erkennt das Vorhandensein einer Signatur. Es behauptet nicht, dass eine Signatur gültig, vertrauenswürdig oder nicht gesperrt ist. Diese Entscheidung gehört zu einem kryptografischen Verifier.
Kommerzieller Kontext
Abschnitt betitelt „Kommerzieller Kontext“Kryptografische CMS-Prüfung, X.509-Pfadvalidierung und OCSP-/CRL-Sperrprüfung werden hinter den Signing-Contracts in den Editionen Pro und Enterprise ausgeliefert. Der Core-Inspector deckt nur die Erkennung des Vorhandenseins ab.