Przejdź do głównej zawartości
getnextpdf.com

Sanityzacja niezaufanych plików PDF: rozbrajanie i rekonstrukcja treści

Spec: ISO 32000-2, §12.6.4

PDF, który przybywa ze świata zewnętrznego, jest kodem w równym stopniu, co dokumentem. Może nieść JavaScript, akcję Launch, osadzony plik wykonywalny oraz strukturę spreparowaną, by potknąć parser. Rozbrajanie i rekonstrukcja treści (CDR, Content Disarm & Reconstruction) traktuje ten plik jako niezaufany, zachowuje tylko to, co bezpieczne, i odbudowuje czysty PDF z ocalałych.

Ta strona wyjaśnia, jak CdrEngine z NextPDF Enterprise to robi — oraz jedną uczciwą rzecz, którą każdy dostawca CDR powinien powiedzieć na głos: odbudowany plik to projekcja bezpieczeństwa oryginału, a nie jego wierna kopia.

Niebezpieczne części PDF nie są egzotyczne. To standardowe funkcje. Specyfikacja definiuje cały katalog akcji — co się dzieje, gdy dokument się otwiera, gdy strona jest pokazywana, gdy pole się zmienia — a ten katalog obejmuje akcje JavaScript i Launch (Spec: ISO 32000-2, §12.6.4). Czytnik, który honoruje format, ochoczo je uruchomi. To przyczółek napastnika: plik jednocześnie doskonale poprawny i doskonale złośliwy.

Filtrowanie po rozszerzeniu pliku nic tu nie daje. Zagrożenie jest wewnątrz dobrze uformowanego PDF, więc jedyną realną obroną jest otworzyć go, zrozumieć i zdjąć aktywną maszynerię, zanim w ogóle dotrze do renderera. To postawa walidacji danych wejściowych, którą opisują wytyczne OWASP dotyczące przesyłania plików i walidacji danych wejściowych: nigdy nie ufaj bajtom i przedkładaj odbudowę znanego-dobrego artefaktu nad skanowanie wrogiego w poszukiwaniu znanych-złych sygnatur.

  • CDR zakłada, że dane wejściowe są wrogie, i wytwarza nowy plik, zamiast łatać stary.
  • CdrEngine::sanitize() uruchamia sześć faz: parsowanie, kontrola dopuszczenia, wykrywanie zagrożeń, filtrowanie, czyszczenie odwołań, odbudowa.
  • Zwraca CdrResult, który mówi ci, co usunięto, czy dokument w ogóle został dopuszczony oraz — jeśli nie — dlaczego został odrzucony.
  • Wyjście to projekcja bezpieczeństwa. Jawnie nie jest kopią dowodową, dopasowaniem skrótu ani artefaktem archiwalnym. To umowa, a nie zastrzeżenie.
  • To wyłącznie Enterprise. Edycja podstawowa NextPDF nie wykonuje CDR.

Własna dokumentacja silnika podaje projektową czerwoną linię w trzech słowach: Security Projection Layer. sanitize() to destrukcyjna, nieodwracalna transformacja. Wolno jej wyrzucać bajty. Czego jej nie wolno, to udawać, że wynik jest tym samym dokumentem.

Potok jest celowo uporządkowany. Każda faza zawęża zaufanie, zanim następna faza na nim zadziała.

  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.
Sześć faz CdrEngine::sanitize(), w kolejności. Dokument jest parsowany, następnie dopuszczany lub odrzucany względem limitów zasobów, następnie skanowany pod kątem zagrożeń; niebezpieczne obiekty są odfiltrowywane, wiszące odwołania są czyszczone, a tylko ocalałe bezpieczne obiekty są serializowane do zupełnie nowego PDF. Oryginalne bajty nigdy nie przechodzą dalej.

Dopuszczenie przed rozbrojeniem. Zanim usunięte zostanie pojedyncze zagrożenie, silnik pyta, czy dokument jest w ogóle wart przetwarzania. CdrPolicy niesie limity — maxObjects, maxPageCount, maxDecodedStreamBytes oraz maxInflationRatio, który broni przed bombami dekompresyjnymi. Dokument, który je przekracza, jest odrzucany, a CdrResult mówi to za pomocą admitted: false oraz rejectionReason. To rozróżnienie jest nośne: „oczyściliśmy go” i „odmówiliśmy mu” to różne wyniki, a typ wyniku trzyma je osobno, by twoje raportowanie błędów też mogło.

Zagrożenia są nazywane, a nie zgadywane. ThreatDetector::detect() odwzorowuje każdą niebezpieczną konstrukcję na ThreatTypeJavaScript, LaunchAction, OpenAction, AdditionalActions, RemoteGoTo, SubmitForm, ImportData, EmbeddedFiles, RichMedia, NamedJavaScript i więcej — z których każdy jest związany z konkretną funkcją PDF. Obiekt, którego nie da się w ogóle sparsować, staje się własnym typem zagrożenia, UnparseableObject, ponieważ obiekt, którego sanityzator nie potrafi odczytać, jest obiektem, za który nie potrafi ręczyć. Każde znalezisko to DetectedThreat niosący numer obrażającego obiektu, więc usunięcie jest precyzyjne.

Usuwanie chroni dokument, a nie tylko ładunek. Niektóre niebezpieczne klucze żyją w katalogu dokumentu (Root) — na przykład /OpenAction, który odpala przy otwarciu. Usunięcie całego obiektu Root, by zabić jeden klucz, zniszczyłoby katalog i po cichu zepsuło plik. Silnik obsługuje je zamiast tego jako zdjęcia klucza w miejscu na katalogu i, jako zabezpieczenie zawodzące bezpiecznie, odmawia wyemitowania odbudowanego dokumentu, który w ogóle utracił swój /Root. Sanityzator, który wytwarza strukturalnie zepsuty plik, raportując sukces, to dokładnie ten tryb awarii, któremu to zabezpieczenie ma zapobiegać.

Poniższy kształt to prawdziwy punkt wejścia. Wręczasz silnikowi surowe bajty i politykę; odzyskujesz CdrResult, który jest uczciwy co do tego, co się stało.

<?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.

Nie ma ścieżki, na której ten kod po cichu oddaje wciąż niebezpieczny plik. Albo dokument jest dopuszczony i rozbrojony, albo jest odrzucony z podaną przyczyną. Lista removedThreats oznacza, że rozbrojenie da się audytować, a nie że jest magiczne.

„CDR to po prostu redakcja z dodatkowymi krokami.”

Nie jest, a mylenie tych dwóch jest niebezpieczne. Redakcja usuwa informację — nazwiska, numery kont, treść, której człowiek nie może zobaczyć. CDR usuwa zdolność — JavaScript, akcję Launch, osadzony ładunek, którego maszyna nie może uruchomić. Mają przeciwne kryteria sukcesu. Redakcja jest poprawna, gdy wrażliwa treść zniknęła, a reszta jest zachowana dosłownie. Rozbrojenie jest poprawne, gdy zagrożenie zniknęło, i jest doskonale skłonne zmienić nieszkodliwą strukturę, by to osiągnąć. Użyj narzędzia, które pasuje do twojej intencji; nie sięgaj po jedno, oczekując gwarancji drugiego.

Drugie nieporozumienie jest takie, że czysta odbudowa dowodzi, iż oryginał był czysty. Nie dowodzi niczego o oryginale. Dowodzi jedynie, że wyjście nie zawiera wykrytego zagrożenia. Dane wejściowe mogły być bronią; zadaniem CDR jest upewnić się, że to, co przekazujesz dalej, nią nie jest.

To część, którą broszury pomijają, więc powiemy ją wprost.

  • Wyjście to projekcja bezpieczeństwa, a nie kopia dowodowa. Źródło CdrEngine niesie to jako architektoniczną czerwoną linię. Zsanityzowany PDF nie może być użyty do prawnego zabezpieczenia dowodów, do porównania skrótu z oryginałem ani jako kopia archiwalna. Transformacja jest destrukcyjna i nieodwracalna z założenia.
  • Wykrywanie ma zakres. CDR usuwa zagrożenia, które potrafi nazwać. To silna, audytowalna warstwa w stosie obrony w głąb — a nie gwarancja, że plik jest wolny od każdej możliwej przyszłej techniki. Trzymaj ją za tą samą walidacją przesyłania, kontrolą typu treści oraz obsługą o najmniejszych uprawnieniach, które opisują wytyczne OWASP dotyczące przesyłania plików.
  • Niektóre polityki są celowo stratne. Włączane opcjonalnie przypadki Strip* usuwają pliki osadzone, podpisy, pola formularzy, warstwy oraz media 3D. Są potężne i usuną legalną treść — na przykład ładunek faktury ZUGFeRD/Factur-X. Są domyślnie wyłączone dokładnie z tego powodu. Włączaj je świadomie.
  • Podpisy nie przeżywają odbudowy. Rekonstrukcja pliku zmienia jego bajty, więc każdy oryginalny podpis cyfrowy nie pasuje już do swojego zakresu bajtów. Rozbrojony dokument jest niepodpisany względem źródła. Jeśli potrzebujesz podpisanego artefaktu, podpisz czyste wyjście jako nową czynność.
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) — strategia sanityzacji, która parsuje niezaufany plik, usuwa aktywne lub niebezpieczne komponenty i odbudowuje czysty plik z bezpiecznej reszty, zamiast skanować w poszukiwaniu znanych-złych sygnatur.
  • Projekcja bezpieczeństwa — zsanityzowane wyjście, które zachowuje wystarczająco dużo ze źródła, by było użyteczne, gwarantując jednocześnie usunięcie zagrożeń. Jest celowo niewierne bajtowo i nieodpowiednie do dowodów, haszowania ani archiwizacji.
  • Kontrola dopuszczenia — brama przedsanityzacyjna, która odrzuca dokumenty przekraczające limity zasobów (liczba obiektów, liczba stron, rozmiar zdekodowanego strumienia, współczynnik inflacji), zanim rozpocznie się jakakolwiek praca rozbrajania.
  • Akcja — konstrukcja PDF, która sprawia, że coś się dzieje na wyzwalacz taki jak otwarcie dokumentu lub zmiana pola; katalog typów akcji (Spec: ISO 32000-2, §12.6.4) obejmuje akcje JavaScript i Launch, kanoniczną powierzchnię zagrożeń CDR.
  • Wiszące odwołanie — wskaźnik do obiektu, który po filtrowaniu już nie istnieje. Faza czyszczenia odwołań usuwa je, dzięki czemu odbudowany plik pozostaje strukturalnie spójny.