ตรวจสอบลายเซ็นที่มีในเอกสารและทำความเข้าใจขอบเขตความเชื่อถือ
ภาพรวมโดยย่อ
หัวข้อที่มีชื่อว่า “ภาพรวมโดยย่อ”ใช้ Core inspector เพื่อตรวจว่า PDF มี signature dictionary หรือไม่ inspector ทำงานแบบออฟไลน์และไม่ใช้ Spectrum sidecar สูตรนี้ยังช่วยทำให้ขอบเขตความเชื่อถือชัดเจนขึ้นด้วย กล่าวคือการตรวจพบลายเซ็นไม่เท่ากับการตรวจสอบยืนยันลายเซ็น การตรวจสอบทางการเข้ารหัสลับ การตรวจสอบ trust-path และการตรวจสอบการเพิกถอนเป็นฟีเจอร์ของ Premium หรือระบบภายนอก
การติดตั้ง
หัวข้อที่มีชื่อว่า “การติดตั้ง”composer require nextpdf/core:^3ภาพรวมเชิงแนวคิด
หัวข้อที่มีชื่อว่า “ภาพรวมเชิงแนวคิด”ลายเซ็น PDF คือ signature field ที่มีค่าเป็น signature dictionary (ISO 32000-2 §12.7.4) รายการ Contents ของ dictionary นี้เก็บ Cryptographic Message Syntax (CMS) SignedData ที่เข้ารหัสแบบ DER (ISO 32000-2 §12.8.1) Quick fallback ของ Inspector ตรวจหา การมีอยู่ ของโครงสร้างดังกล่าวด้วยการสแกน signature marker โดย ไม่ แยกวิเคราะห์ CMS ไม่คำนวณ byte-range digest ใหม่ (ซึ่งไม่รวมค่าลายเซ็น — ISO 32000-2 §12.8.1) ไม่ตรวจสอบ certificate chain และไม่ตรวจสอบการเพิกถอน
พื้นผิว API
หัวข้อที่มีชื่อว่า “พื้นผิว API”เรียก new Inspector() แล้วตามด้วย ->inspect(string $pdfData, InspectConfig $config) ใช้ InspectConfig::quick() สำหรับ PHP fallback แบบออฟไลน์ InspectDepth::Standard/Full ต้องใช้ Spectrum sidecar และจะ fail closed (INSPECT-SIDECAR-001) เมื่อไม่มี sidecar ผลลัพธ์เป็น value object ชนิด InspectResult สำหรับเวิร์กโฟลว์นี้ ให้ใช้ $hasSigned เพื่อตรวจการมีอยู่ของลายเซ็น และใช้ $isEncrypted กับ $pdfVersion ได้ด้วย
ตัวอย่างโค้ด — เริ่มต้นอย่างรวดเร็ว
หัวข้อที่มีชื่อว่า “ตัวอย่างโค้ด — เริ่มต้นอย่างรวดเร็ว”<?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";ตัวอย่างโค้ด — สำหรับใช้งานจริง
หัวข้อที่มีชื่อว่า “ตัวอย่างโค้ด — สำหรับใช้งานจริง”โปรแกรมแบบ self-contained นี้ทำงานใน cookbook harness และสอดคล้องกับ examples/37-inspect-existing-signature.php โปรแกรมนี้ตรวจสอบตัวอย่าง corpus ที่ทราบว่ามีการเซ็นกำกับ และเอกสารที่ไม่มีลายเซ็นซึ่งสร้างขึ้นใหม่ เพื่อให้เห็นทั้งสองสาขาของ presence flag ได้ จากนั้นจึงส่งต่อผลการตัดสินไปยังขั้นถัดไป การมีอยู่เป็นข้อมูลนำเข้าสำหรับการกำหนดเส้นทาง ไม่ใช่ผลการตัดสินเรื่องความเชื่อถือ ไฟล์จะถูกส่งต่อไปยัง cryptographic verifier (Pro หรือภายนอก) และในขั้นตอนนี้ยังไม่ถือว่าไฟล์นั้นเชื่อถือได้
<?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);STDOUT ที่คาดหวัง (สาขาที่มีการเซ็นกำกับจะถูกข้ามหากไม่มีตัวอย่าง corpus):
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 : noกรณีขอบและข้อควรระวัง
หัวข้อที่มีชื่อว่า “กรณีขอบและข้อควรระวัง”- การมีอยู่ไม่ใช่ความถูกต้อง
$hasSignedรายงานว่ามี signature dictionary อยู่ โดยไม่ตรวจสอบโครงสร้าง CMS, byte-range digest, certificate chain ของใบรับรองที่ใช้เซ็น หรือการเพิกถอน ไฟล์ที่ถูกแก้ไขดัดแปลงก็ยังสามารถรายงานhasSigned = trueได้ อย่าถือว่าการมีอยู่เป็นหลักฐานของความสมบูรณ์หรือการเป็นผู้สร้างเด็ดขาด - สิ่งที่การตรวจสอบเต็มรูปแบบต้องการ การตัดสินใจที่สมบูรณ์จะคำนวณ byte-range digest ใหม่ (ISO 32000-2 §12.8.1) ตรวจสอบ CMS SignedData สร้างและตรวจสอบ X.509 path ไปยัง trusted anchor และตรวจสอบการเพิกถอนผ่าน Online Certificate Status Protocol (OCSP) หรือ certificate revocation list (CRL) signature timestamp หากมีอยู่ จะถูกตรวจสอบกับ imprint ของตนเองที่ครอบ signature value octets (ETSI EN 319 122-1 §5.3) การดำเนินการเหล่านี้ทำงานอยู่เบื้องหลัง signing contracts การนำไปใช้สำหรับใช้งานจริงมาพร้อมใน Pro และ Enterprise ส่วน validator ภายนอกเป็นอีกเส้นทางหนึ่งที่รองรับ
- ระดับความลึกของการตรวจสอบ
InspectConfig::quick()เป็นระดับเดียวที่ทำงานได้โดยไม่ต้องใช้ Spectrum sidecarStandard/Fullจะ throwINSPECT-SIDECAR-001เมื่อไม่มี sidecar ให้ใช้งาน - ข้อมูลนำเข้าว่างเปล่า สตริงว่างจะ throw inspect exception พร้อมข้อความ “PDF data must not be empty” จึงควรป้องกันขั้นตอนการอ่านไว้ก่อน
- ลายเซ็น / timestamp หลายรายการ presence flag ไม่นับจำนวนลายเซ็นและไม่แยกแยะ approval signature ออกจาก document timestamp (ซึ่งเก็บอยู่ใน
unsignedAttrsตาม RFC 5652 §5.3 เช่นกัน) ให้ใช้ verifier เฉพาะทางเมื่อจำนวนหรือผลการตัดสินรายลายเซ็นมีความสำคัญ
ประสิทธิภาพ
หัวข้อที่มีชื่อว่า “ประสิทธิภาพ”Quick fallback สแกนไบต์ของเอกสารภายในขอบเขตจำกัด โดยไม่แยกวิเคราะห์ object graph ทั้งหมด ใช้สำหรับคัดกรองไฟล์ขาเข้าอย่างรวดเร็วก่อนส่งต่อไปยัง verifier ที่หนักกว่า
หมายเหตุด้านความปลอดภัย
หัวข้อที่มีชื่อว่า “หมายเหตุด้านความปลอดภัย”inspector เป็นเครื่องมือคัดกรอง ไม่ใช่ขอบเขตความเชื่อถือ ค่า hasSigned ที่เป็นบวกต้องไม่ถูกใช้เป็นเงื่อนไขกำหนดการตัดสินใจเรื่องความเชื่อถือโดยลำพังเด็ดขาด
ถิ่นที่อยู่ของข้อมูลและการลดความเสี่ยง PII
หัวข้อที่มีชื่อว่า “ถิ่นที่อยู่ของข้อมูลและการลดความเสี่ยง PII”การตรวจสอบทำงานภายในกระบวนการทั้งหมด ไม่มีไบต์ของเอกสารถูกส่งออกจากโฮสต์ Quick fallback อ่านเฉพาะ structural marker ไม่ใช่ข้อความในเอกสาร จึงไม่สกัดหรือส่งข้อมูลที่ระบุตัวบุคคลได้ (PII)
Telemetry ที่ปลอดภัยและการล้างข้อมูลใน Log
หัวข้อที่มีชื่อว่า “Telemetry ที่ปลอดภัยและการล้างข้อมูลใน Log”Inspector รองรับ PSR-3 logger แบบเลือกได้ โดยบันทึก log ของเส้นทางที่เลือก (“Spectrum unavailable, using PHP fallback”) ไม่ใช่เนื้อหาเอกสาร อย่าบันทึก log ไบต์ของ PDF ที่ตรวจสอบหรือ InspectResult ต้นฉบับ หากเอกสารมีความอ่อนไหว
แบบจำลองภัยคุกคาม
หัวข้อที่มีชื่อว่า “แบบจำลองภัยคุกคาม”กรณีที่พิจารณา ได้แก่ ไฟล์ที่ถูกแก้ไขดัดแปลงซึ่งแสดง signature dictionary ที่ถูกต้องตามไวยากรณ์ (inspector รายงานการมีอยู่ แต่ไม่ยืนยันความสมบูรณ์อย่างชัดเจน) และไฟล์ที่ไม่มีลายเซ็น (รายงานว่าไม่มีอย่างถูกต้อง) สิ่งที่ไม่ได้ยืนยันคือ ลายเซ็นที่ตรวจพบใด ๆ นั้นถูกต้องทางการเข้ารหัสลับ เชื่อถือได้ หรือไม่ถูกเพิกถอน — สิ่งเหล่านั้นเป็นหน้าที่ของ verifier
พฤติกรรมในโหมด FIPS
หัวข้อที่มีชื่อว่า “พฤติกรรมในโหมด FIPS”Quick fallback ไม่ดำเนินการเข้ารหัสลับใด ๆ ดังนั้นโหมด Federal Information Processing Standards (FIPS) จึงไม่เกี่ยวข้องกับสูตรนี้ การตรวจสอบทางการเข้ารหัสลับ (Premium/ภายนอก) คือจุดที่ FIPS provider chain มีความสำคัญ
ความสอดคล้อง
หัวข้อที่มีชื่อว่า “ความสอดคล้อง”| ข้อความระบุ | ข้อกำหนด | ข้อ | รหัสอ้างอิง (reference_id) |
|---|---|---|---|
| ค่าของ signature field คือ signature dictionary | ISO 32000-2 | §12.7.4 | |
Contents เก็บ DER CMS SignedData และ Contents ของ document-timestamp เก็บ TimeStampToken | ISO 32000-2 | §12.8.1 | |
| การตรวจสอบจะคำนวณ digest ใหม่บน byte range โดยไม่รวมค่าลายเซ็น | ISO 32000-2 | §12.8.1 | |
| imprint ของ signature timestamp ครอบ signature value octets ของ SignerInfo | ETSI EN 319 122-1 | §5.3 | |
| timestamp ถูกเก็บไว้ใน unsignedAttrs ของ SignerInfo | RFC 5652 | §5.3 |
สูตรนี้ตรวจหาลายเซ็น โดยไม่ยืนยันว่าลายเซ็นใด ๆ ถูกต้อง เชื่อถือได้ หรือไม่ถูกเพิกถอน cryptographic verifier เป็นผู้ให้ผลการตัดสินดังกล่าว
บริบทเชิงพาณิชย์
หัวข้อที่มีชื่อว่า “บริบทเชิงพาณิชย์”การตรวจสอบ CMS ทางการเข้ารหัสลับ การตรวจสอบ X.509 path และการตรวจสอบการเพิกถอนแบบ OCSP/CRL มาพร้อมอยู่เบื้องหลัง signing contracts ในรุ่น Pro และ Enterprise ส่วน Core inspector ครอบคลุมเฉพาะการตรวจหาการมีอยู่เท่านั้น