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

การล้าง PDF ที่ไม่น่าเชื่อถือ: การปลดอาวุธและประกอบเนื้อหาขึ้นใหม่

Spec: ISO 32000-2, §12.6.4

PDF ที่มาถึงจากโลกภายนอกเป็นโค้ดพอๆกับที่เป็นเอกสาร มันสามารถพก JavaScript, Launch action, executable ที่ฝังตัว และโครงสร้างที่ออกแบบมาเพื่อทำให้ตัวแจงรูปสะดุด Content Disarm & Reconstruction (CDR) ปฏิบัติต่อไฟล์นั้นในฐานะที่ไม่น่าเชื่อถือ เก็บไว้เฉพาะสิ่งที่ปลอดภัย และประกอบ PDF สะอาดขึ้นใหม่จากสิ่งที่รอด

หน้านี้อธิบายว่า CdrEngine ของ NextPDF Enterprise ทำสิ่งนั้นอย่างไร — และสิ่งซื่อสัตย์อย่างหนึ่งที่ผู้ขาย CDR ทุกรายควรพูดออกมาดังๆ: ไฟล์ที่ประกอบขึ้นใหม่คือ การฉายภาพด้านความปลอดภัย ของต้นฉบับ ไม่ใช่สำเนาที่ซื่อตรงของมัน

ส่วนที่อันตรายของ PDF ไม่ใช่ของแปลกประหลาด มันคือคุณสมบัติมาตรฐาน ข้อกำหนดนิยามแคตตาล็อกทั้งชุดของ action — สิ่งที่เกิดขึ้นเมื่อเอกสารเปิด เมื่อหน้าถูกแสดง เมื่อฟิลด์เปลี่ยน — และแคตตาล็อกนั้นรวมถึง JavaScript และ Launch action (Spec: ISO 32000-2, §12.6.4) โปรแกรมอ่านที่เคารพรูปแบบจะรันมันอย่างยินดี นั่นคือฐานที่มั่นของผู้โจมตี: ไฟล์ที่ถูกต้องสมบูรณ์แบบและประสงค์ร้ายอย่างสมบูรณ์แบบในเวลาเดียวกัน

การกรองตามนามสกุลไฟล์ไม่ช่วยอะไรตรงนี้ ภัยคุกคามอยู่ภายใน PDF ที่มีรูปแบบดี ดังนั้นการป้องกันที่แท้จริงเพียงอย่างเดียวคือการเปิดมัน เข้าใจมัน และลอกกลไกที่ทำงานออกก่อนที่มันจะไปถึงตัวเรนเดอร์ นี่คือท่าทีการตรวจสอบอินพุตที่คำแนะนำเรื่องการอัปโหลดไฟล์และการตรวจสอบอินพุตของ OWASP อธิบายไว้: อย่าเชื่อไบต์เลย และให้เลือกการประกอบอาร์ติแฟกต์ที่รู้ว่าดีขึ้นใหม่ มากกว่าการสแกนไฟล์ที่เป็นปฏิปักษ์เพื่อหาลายเซ็นที่รู้ว่าไม่ดี

  • CDR สันนิษฐานว่าอินพุตเป็นปฏิปักษ์และผลิตไฟล์ ใหม่ แทนที่จะแก้ไขไฟล์เก่า
  • CdrEngine::sanitize() รันหกระยะ: แจงรูป admission control การตรวจจับภัยคุกคาม กรอง ล้าง reference ประกอบใหม่
  • มันคืน CdrResult ที่บอกคุณว่าอะไรถูกนำออก ว่าเอกสารถูก รับเข้า หรือไม่ และ — หากไม่ — เหตุใดมันจึงถูกปฏิเสธ
  • เอาต์พุตคือ การฉายภาพด้านความปลอดภัย มันชัดเจนว่า ไม่ใช่ สำเนาที่เป็นพยานหลักฐาน, การจับคู่ hash หรืออาร์ติแฟกต์สำหรับการเก็บถาวร นั่นเป็นสัญญา ไม่ใช่ข้อแม้
  • นี่เป็น Enterprise เท่านั้น คอร์ของ NextPDF ไม่ทำ CDR

เอกสารของเอนจินเองระบุเส้นแดงในการออกแบบเป็นสามคำ: Security Projection Layer sanitize() คือการแปลงที่ทำลายและย้อนกลับไม่ได้ มันได้รับอนุญาตให้ทิ้งไบต์ สิ่งที่มันไม่ได้รับอนุญาตให้ทำคือการแกล้งทำว่าผลลัพธ์เป็นเอกสารเดียวกัน

ไปป์ไลน์ถูกเรียงลำดับโดยตั้งใจ แต่ละระยะลดความเชื่อถือลงก่อนที่ระยะถัดไปจะกระทำต่อมัน

  1. ParsePdfReader builds the object graph from the raw bytes. A parse failure is a rejection, not a best-effort guess.
  2. Admission controlResource limits are checked first — object count, page count, decoded-stream size, per-stream inflation ratio. Over-budget input is rejected, never sanitised.
  3. DetectThreatDetector walks the graph and records each dangerous feature as a DetectedThreat with its object number and type.
  4. FilterObjects are partitioned into safe and removed. Catalog-level keys are stripped in place so the document catalog itself survives.
  5. Reference scrubPointers to removed objects are cleaned so the rebuilt file has no dangling references.
  6. RebuildCdrRebuilder serialises only the safe objects into a new PDF. The output is fresh structure, not an edited original.
The six phases of CdrEngine::sanitize(), in order. The document is parsed, then admitted or rejected against resource limits, then scanned for threats; dangerous objects are filtered out, dangling references are scrubbed, and only the surviving safe objects are serialised into a brand-new PDF. The original bytes never carry through.

รับเข้าก่อนปลดอาวุธ ก่อนที่ภัยคุกคามแม้แต่ตัวเดียวจะถูกนำออก เอนจินถามว่าเอกสารคุ้มค่าที่จะประมวลผลหรือไม่ CdrPolicy พกขีดจำกัด — maxObjects, maxPageCount, maxDecodedStreamBytes และ maxInflationRatio ที่ป้องกันระเบิดการคลายการบีบอัด เอกสารที่ทะลุขีดจำกัดเหล่านั้นจะถูก ปฏิเสธ และ CdrResult ก็บอกเช่นนั้นด้วย admitted: false และ rejectionReason ความแตกต่างนี้รับน้ำหนัก: “เราทำความสะอาดมัน” และ “เราปฏิเสธมัน” เป็นผลลัพธ์ที่ต่างกัน และชนิดของผลลัพธ์แยกทั้งสองออกจากกันเพื่อให้การรายงานข้อผิดพลาดของคุณทำได้เช่นกัน

ภัยคุกคามถูกตั้งชื่อ ไม่ถูกเดา ThreatDetector::detect() แมปแต่ละโครงสร้างที่อันตรายเข้ากับ ThreatTypeJavaScript, LaunchAction, OpenAction, AdditionalActions, RemoteGoTo, SubmitForm, ImportData, EmbeddedFiles, RichMedia, NamedJavaScript และอื่นๆ — แต่ละตัวผูกกับคุณสมบัติ PDF ที่เฉพาะเจาะจง อ็อบเจกต์ที่แจงรูปไม่ได้เลยกลายเป็นชนิดภัยคุกคามของตัวเอง คือ UnparseableObject เพราะอ็อบเจกต์ที่ตัวล้างอ่านไม่ได้คืออ็อบเจกต์ที่มันรับรองไม่ได้ ทุกสิ่งที่พบเป็น DetectedThreat ที่พกหมายเลขอ็อบเจกต์ที่เป็นปัญหา ดังนั้นการนำออกจึงแม่นยำ

การนำออกปกป้องเอกสาร ไม่ใช่แค่เพย์โหลด คีย์ที่อันตรายบางตัวอยู่บน document catalog (Root) — /OpenAction ที่ทำงานเมื่อเปิด เป็นต้น การนำอ็อบเจกต์ Root ทั้งตัวออกเพื่อฆ่าคีย์เดียวจะทำลาย catalog และทำให้ไฟล์เสียอย่างเงียบๆ เอนจินจัดการสิ่งเหล่านี้ในฐานะ การลอกคีย์ในที่เดิม บน catalog แทน และในฐานะตัวคุ้มกันแบบ fail-closed มันปฏิเสธที่จะปล่อยเอกสารที่ประกอบขึ้นใหม่ซึ่งสูญเสีย /Root ไปเลย ตัวล้างที่ผลิตไฟล์ที่เสียเชิงโครงสร้างในขณะที่รายงานว่าสำเร็จคือรูปแบบความล้มเหลวที่ตัวคุ้มกันนี้มีอยู่เพื่อป้องกันพอดี

รูปแบบด้านล่างคือจุดเข้าจริง คุณส่งไบต์ดิบและ policy ให้เอนจิน คุณได้รับ CdrResult ที่ซื่อสัตย์เกี่ยวกับสิ่งที่เกิดขึ้นกลับมา

<?php
declare(strict_types=1);
use NextPDF\Enterprise\Security\Cdr\CdrEngine;
use NextPDF\Enterprise\Security\Cdr\CdrPolicy;
$engine = new CdrEngine();
// Standard policy removes the known active threats — JavaScript, Launch
// actions, remote go-to, form submit/import, and the rest — while leaving
// the lossy opt-in "Strip*" cases off by default.
$result = $engine->sanitize($untrustedPdfBytes, CdrPolicy::standard());
if (!$result->admitted) {
// Rejected by admission control (e.g. object/page limit, zip bomb).
// This is NOT a sanitised document. Do not serve it; report the reason.
throw new \RuntimeException($result->rejectionReason);
}
if ($result->hadThreats()) {
// The disarmed bytes are safe to render. Each removed threat carries its
// type and object number for your audit log — never silently.
foreach ($result->removedThreats as $threat) {
error_log(\sprintf(
'CDR removed %s in object %d',
$threat->type->value,
$threat->objectNumber,
));
}
}
$cleanBytes = $result->sanitizedPdf; // The security projection. Not the original.

ไม่มีเส้นทางใดที่โค้ดนี้ส่งไฟล์ที่ยังอันตรายกลับมาอย่างเงียบๆ ไม่เอกสารถูกรับเข้าและปลดอาวุธ ก็ถูกปฏิเสธพร้อมเหตุผลที่ระบุไว้ รายการ removedThreats หมายความว่าการปลดอาวุธตรวจสอบได้ ไม่ใช่เวทมนตร์

“CDR ก็แค่การปิดบังข้อมูลที่มีขั้นตอนเพิ่ม”

มันไม่ใช่ และการเหมารวมทั้งสองเข้าด้วยกันเป็นอันตราย การปิดบังข้อมูลนำ ข้อมูล ออก — ชื่อ หมายเลขบัญชี เนื้อหาที่มนุษย์ต้องไม่เห็น CDR นำ ความสามารถ ออก — JavaScript, Launch action, เพย์โหลดที่ฝังตัวที่เครื่องจักรต้องไม่รัน ทั้งสองมีเกณฑ์ความสำเร็จที่ตรงกันข้าม การปิดบังข้อมูลถูกต้องเมื่อ เนื้อหา ที่ละเอียดอ่อนหายไปและส่วนที่เหลือถูกรักษาไว้คำต่อคำ การปลดอาวุธถูกต้องเมื่อ ภัยคุกคาม หายไป และมันยินดีอย่างยิ่งที่จะเปลี่ยนแปลงโครงสร้างที่ไม่เป็นอันตรายเพื่อไปให้ถึงจุดนั้น จงใช้เครื่องมือที่ตรงกับเจตนาของคุณ อย่าคว้าตัวหนึ่งโดยคาดหวังการรับประกันของอีกตัว

ความเข้าใจผิดข้อที่สองคือการประกอบใหม่ที่สะอาดพิสูจน์ว่าต้นฉบับสะอาด มันไม่ได้พิสูจน์อะไรเกี่ยวกับต้นฉบับ มันพิสูจน์เพียงว่า เอาต์พุต ไม่มีภัยคุกคามที่ตรวจพบ อินพุตอาจเป็นอาวุธ งานของ CDR คือทำให้แน่ใจว่าสิ่งที่คุณส่งต่อไปยังปลายทางไม่ใช่อาวุธ

นี่คือส่วนที่โบรชัวร์ข้าม ดังนั้นเราจะพูดมันอย่างตรงไปตรงมา

  • เอาต์พุตคือการฉายภาพด้านความปลอดภัย ไม่ใช่สำเนาที่เป็นพยานหลักฐาน ซอร์สของ CdrEngine พกสิ่งนี้ไว้ในฐานะเส้นแดงเชิงสถาปัตยกรรม PDF ที่ล้างแล้ว ต้องไม่ ถูกใช้สำหรับการเก็บรักษาพยานหลักฐานทางกฎหมาย สำหรับการเปรียบเทียบ hash กับต้นฉบับ หรือเป็นสำเนาสำหรับการเก็บถาวร การแปลงเป็นแบบทำลายและย้อนกลับไม่ได้โดยการออกแบบ
  • การตรวจจับมีขอบเขต CDR นำภัยคุกคามที่มันรู้วิธีตั้งชื่อออก มันเป็นชั้นที่แข็งแรงและตรวจสอบได้ในสแต็ก defence-in-depth — ไม่ใช่การรับประกันว่าไฟล์ปลอดจากทุกเทคนิคที่เป็นไปได้ในอนาคต จงเก็บมันไว้หลังการตรวจสอบการอัปโหลด การตรวจ content-type และการจัดการแบบ least-privilege เดียวกันกับที่คำแนะนำเรื่องการอัปโหลดไฟล์ของ OWASP อธิบายไว้
  • บาง policy สูญเสียข้อมูลโดยตั้งใจ กรณี Strip* แบบ opt-in นำไฟล์ฝังตัว ลายเซ็น ฟิลด์ฟอร์ม เลเยอร์ และสื่อ 3D ออก สิ่งเหล่านั้นทรงพลังและมันจะลบเนื้อหาที่ถูกต้องตามกฎหมาย — เพย์โหลดใบแจ้งหนี้ ZUGFeRD/Factur-X เช่นนั้น พวกมันถูกปิดไว้ตามค่าเริ่มต้นด้วยเหตุผลนั้นพอดี เปิดมันโดยรู้ตัว
  • ลายเซ็นไม่รอดจากการประกอบใหม่ การประกอบไฟล์ขึ้นใหม่เปลี่ยนไบต์ของมัน ดังนั้นลายเซ็นดิจิทัลต้นฉบับใดๆจึงไม่ตรงกับ byte range ของมันอีกต่อไป เอกสารที่ปลดอาวุธแล้วเป็นเอกสารที่ไม่ลงนามเทียบกับต้นทาง หากคุณต้องการอาร์ติแฟกต์ที่ลงนามแล้ว จงลงนามเอาต์พุตที่ สะอาด ในฐานะการกระทำใหม่
Content Disarm & Reconstruction (CDR) — edition availability
EditionAvailability
Core

Not available. NextPDF core does not perform CDR. It parses, renders, and writes PDFs; it does not threat-detect or rebuild untrusted input.

Pro

Not available in the Pro edition.

Enterprise

Available via CdrEngine. The disarmed output is a security projection of the source — it must not be treated as an evidentiary, hash-comparable, or archival copy of the original.

  • CDR (Content Disarm & Reconstruction) — กลยุทธ์การล้างที่แจงรูปไฟล์ที่ไม่น่าเชื่อถือ นำส่วนประกอบที่ทำงานหรืออันตรายออก และประกอบไฟล์สะอาดขึ้นใหม่จากส่วนที่เหลือที่ปลอดภัย แทนที่จะสแกนหาลายเซ็นที่รู้ว่าไม่ดี
  • Security projection — เอาต์พุตที่ล้างแล้วซึ่งรักษา พอเพียง ของต้นทางให้มีประโยชน์ขณะที่รับประกันการนำภัยคุกคามออก มันไม่ซื่อตรงต่อไบต์โดยตั้งใจและไม่เหมาะสำหรับพยานหลักฐาน การคำนวณ hash หรือการเก็บถาวร
  • Admission control — ประตูก่อนการล้างที่ปฏิเสธเอกสารที่เกินขีดจำกัดทรัพยากร (จำนวนอ็อบเจกต์ จำนวนหน้า ขนาดสตรีมที่ถอดรหัสแล้ว อัตราการขยายตัว) ก่อนที่งานปลดอาวุธใดๆจะเริ่ม
  • Action — โครงสร้าง PDF ที่ทำให้บางสิ่งเกิดขึ้นเมื่อมีตัวกระตุ้น เช่น การเปิดเอกสารหรือการเปลี่ยนฟิลด์ แคตตาล็อกชนิด action (Spec: ISO 32000-2, §12.6.4) รวมถึง JavaScript และ Launch action ซึ่งเป็นพื้นผิวภัยคุกคามตามแบบฉบับของ CDR
  • Dangling reference — pointer ไปยังอ็อบเจกต์ที่ไม่มีอยู่อีกต่อไปหลังการกรอง ระยะ reference-scrub นำสิ่งเหล่านี้ออกเพื่อให้ไฟล์ที่ประกอบขึ้นใหม่คงสอดคล้องกันเชิงโครงสร้าง