U PDFium Component buildovima prije v3.126.2, TPdf.AnalyzeSignatureRevisions mogao je stvarnu izmjenu sadržaja stranice ocijeniti kao dopuštenu promjenu anotacije pod DocMDP P=3, jer je njegov graf uloga revizija /P povratnu referencu signature widgeta na njegovu stranicu tretirao kao vlasništvo. Od v3.126.2 PDFium Component razdvaja navigacijske grane od grana stvarnog vlasništva, pa sadržaj stranice ostaje sadržaj stranice. Bug izvještaj iza ovog popravka na papiru izgleda bezopasno. Certificirani ugovor dopušta anotacije, protustranka doda jedno inkrementalno spremanje, i analyzer kaže da je svaka kasnija promjena dopuštena. Onda netko usporedi renderirane stranice i iznos plaćanja na stranici 2 je drugačiji
Ovaj članak nastavak je iz očiju napadača na pregled analize promjena revizija nakon potpisa, pa preskače osnove obnove revizija i DocMDP ocjenjivanja i ide pravo na graf objekata: kako je vlasništvo modelirano, zašto smjer grane odlučuje o sigurnosnoj odluci, što je promijenjeno u v3.126.2 i kako revizirati vlastitu logiku prihvaćanja
Zašto je izmjena stranice prošla kao promjena anotacije pod DocMDP P=3?
Izmjena stranice prošla je jer je stari graf uloga slijedio svaku indirektnu referencu u rječniku kao da referencirani objekt pripada onome koji ga referencira, a signature widget pokazuje natrag na svoju stranicu. Rječnik anotacije nosi /P, indirektnu referencu na objekt stranice na kojem sjedi (ISO 32000-1 §12.5.2). Taj unos je navigacijski nagovještaj. Widget ne posjeduje stranicu; stranica posjeduje widget kroz svoje polje /Annots
Analyzer svakom objektu dodjeljuje skup bitova uloga prije nego ocijeni kasnije promjene: stranica, anotacija, forma i validacijski materijal. Korijenski objekti ulogu dobivaju iz vlastitog rječnika, a uloga se zatim širi na sve što referenciraju. U staroj propagaciji lanac je išao ovako:
- Signature widget je
/Subtype /Widgets/FT /Sig, pa dobiva ulogu anotacije - Widgetov
/Pgura ulogu anotacije na rječnik stranice, koji već ima ulogu stranice - Stranica obje uloge gura u
/Contents,/Resourcesi, kroz/Parent, gore u Pages stablo i preko svake sestrinske stranice - Rječnik toka sadržaja poput
<< /Length 812 >>nema/Type, pa je klasifikator pao natrag na bitove uloga i provjerio ulogu anotacije prije uloge stranice
Izmijenjeni tok sadržaja zato je izašao kao prckAnnotation. Pod ISO 32000-1 §12.8.2.2, DocMDP P=3 dopušta promjene anotacija, pa je odluka bila prdAllowed, a izvještaj se zbrojio u prasAllowed. Ista datoteka pod P=2 bila je odbijena, ali samo slučajno: P=2 zabranjuje promjene anotacija, pa je pogrešno označeni tok odbijen zbog krivog razloga. Fiksna petlja propagacije od četiri prolaza dodala je i drugu slabost. Sadržaj koji stiže kroz indirektno polje, ili kroz dugi lanac čiji se brojevi objekata vraćaju unatrag, možda nikad ne dobije nikakvu ulogu
Zašto validator potpisa mora pitati tko posjeduje objekt?
Validator potpisa mora pitati tko posjeduje objekt jer PDF inkrementalna ažuriranja (ISO 32000-1 §7.5.6) dopuštaju svakome da doda reviziju koja redefinira postojeći broj objekta, a redefinirano tijelo ne najavljuje što je. Potpis i dalje prolazi verifikaciju, jer pokriva samo bajtove vlastite revizije. Svaka obrana od manipulacije nakon potpisa zato ovisi o tome da se svaki promijenjeni objekt mapira u strukturu koja ga koristi, pa da se pita je li potpisnik dopustio da se ta struktura mijenja
Nekoliko objavljenih klasa napada radi baš u tom prorezu. Napadi inkrementalnim spremanjem dodaju reviziju koja mijenja sadržaj stranice i oslanjaju se na to da verifier provjerava samo potpisani bajtni raspon. Shadow napadi posađuju skriveni sadržaj prije potpisivanja i aktiviraju ga poslije malom, nevina izgledajućom promjenom. Napadi na certificirane dokumente zlostavljaju činjenicu da P=2 i P=3 izričito dopuštaju neke kasnije izmjene, pa zabranjenu izmjenu obuku u dopuštenu. Verifier koji klasificira objekte po oznakama poput /Type /Annot, ili po bilo kojem putu referenci koji slučajno dođe do njih, izložen je trećoj klasi: napadaču treba samo jedna dopuštena struktura koja može doći do zabranjene
Zato pitanje nije što se objekata promijenilo, nego tko ih posjeduje. Tok sadržaja dohvaćen sa stranice kroz /Contents sadržaj je stranice ma što sve pokazivalo na njega. Anotacija koja pokazuje natrag na stranicu kroz /P govori gdje anotacija živi, ne što posjeduje
Kako PDFium Component v3.126.2 modelira vlasništvo?
PDFium Component v3.126.2 povratne reference tretira kao navigaciju, drži ih izvan propagacije uloga, i odlučuje koji ključevi računaju se kao navigacija prema strukturalnoj ulozi rječnika koji ih drži, a ne prema samom imenu ključa. Tablica sažima navigacijske ključeve koji više ne nose vlasništvo
| Vlasnički rječnik | Ključevi tretirani kao navigacija | Referenca na specifikaciju |
|---|---|---|
| Stranica ili Pages čvor | /Parent, /Kids, /Annots | ISO 32000-1 §7.7.3 |
| Anotacija ili widget | /P | ISO 32000-1 §12.5.2 |
| Rječnik widgeta ili polja | /Parent | ISO 32000-1 §12.7.3 |
Globalno filtriranje po imenu ključa stvorilo bi novu rupu. Resurs fonta ili XObject legitimno se može zvati /P, /Parent ili /Annots, a /Resources rječnik koji bi svoj unos /P izbacio iz propagacije dopustio bi napadaču da sakrije stranicom posjedovani XObject iza nevinog imena resursa. U v3.126.2 navigacijski filter primjenjuje se samo kad vlasnički rječnik stvarno jest stranica, Pages čvor, anotacija, widget ili polje. Ako jedan od tih rječnika nosi duplicirani navigacijski ključ, poput dva unosa /P u widgetu, analyzer ne pogađa koju bi kopiju preglednik koristio; izgradnja uloga pada, a potpis postaje Indeterminate
Nekoliko daljnjih pravila zatvara preostale rute preoznačavanja:
- Pages čvorovi sami po sebi korijeni su uloge stranice, pa resursi naslijeđeni iz Pages stabla (ISO 32000-1 §7.7.3.4) ulaze u kontekst stranice kroz stvarno vlasništvo, ne kroz
/Parentšetnju od dječje stranice - Uloga anotacije ili forme koja stigne do rječnika kataloga, Pages čvora, stranice, anotacije ili polja tu staje, jer ti strukturalni objekti uspostavljaju vlastite uloge, a uloga dolazećeg sadržaja ne smije ih nadjačati
- Uloga stranice autoritativna je tijekom klasifikacije: objekt u vlasništvu stranice jest
prckPageContentčak i ako ga kasnija revizija prereše s krivotvorenim/FT, oznakom/Type /Annot, ili ga podijeli s appearance tokom - Form XObject korišten samo kao appearance polja ili anotacije zadržava svoju kategoriju forme ili anotacije, pa se uobičajena regeneracija appearancea nakon popunjavanja forme i dalje ocjenjuje pod normalnim pravilima dopuštenja
- Widget bez vlastitog
/FTrazrješuje naslijeđeni tip polja kroz lanac/Parent, a nerazrješiv lanac obara izgradnju uloga umjesto da po zadanom odabere anotaciju - Bitovi uloga iz svake kasnije revizije spajaju se u uloge pokrivene revizije, pa kasnije ažuriranje ne može obrisati raniji odnos vlasništva stranice time što prvo odvoji tok, a zatim ga izmijeni
Fiksna točka umjesto fiksnog broja prolaza
Dohvatljivost uloga u v3.126.2 radi kao radni red koji se ponavlja dok nijedan objekt ne dobije novi bit uloge, što je prava fiksna točka bez obzira na dubinu lanca ili numeriranje objekata. Indirektna polja poput polja /Contents spremljenog kao vlastiti objekt također se prolaze. Svaki objekt može dobiti najviše četiri različita bita uloga, pa je red ograničen na četiri unosa po broju objekta; prekoračenje tog proračuna podiže prrResourceLimitExceeded. Referenca na slobodni objekt, nepoklapanje generacije ili slomljeno zaglavlje objekta podiže prrMalformedRevisionChain, a sadržaj unutar komprimiranog toka objekata podiže prrCompressedObjectUnresolved. Svaki od tih padova završava s prasIndeterminate, nikad s dopuštenom odlukom, i kad pad nastane tijekom izgradnje uloga pokrivene revizije, potpis uopće ne izvještava Changes
Sljedeća rutina ispisuje izmjene sadržaja stranice koje prežive ovu analizu. Promjena prckPageContent nikad se ne ocjenjuje prdAllowed: DocMDP P=1, 2 ili 3 čini je prdDisallowed, a potpis bez DocMDP-a ocjenjuje je prdSuspicious
uses
SysUtils, TypInfo, PDFium, FPdfPades;
procedure ListPageContentEdits(const FileName: string);
const
ShadowTag: array[Boolean] of string = (' (unreferenced shadow)', '');
var
Pdf: TPdf;
Report: TPadesRevisionAnalysisReport;
Sig: TPadesSignatureRevisionAnalysis;
Change: TPadesRevisionObjectChange;
i, j: Integer;
begin
Pdf := TPdf.Create(nil);
try
Pdf.FileName := FileName;
Pdf.Active := True;
Report := Pdf.AnalyzeSignatureRevisions; // zapis, nema što osloboditi
for i := 0 to High(Report.Signatures) do
begin
Sig := Report.Signatures[i];
if not (prrPageContentChanged in Sig.Risks) then
Continue;
Writeln(Format('Signature %d (DocMDP P=%d): page content changed',
[Sig.SignatureIndex, Sig.DocMdpPermission]));
for j := 0 to High(Sig.Changes) do
begin
Change := Sig.Changes[j];
if Change.Kind <> prckPageContent then
Continue;
Writeln(Format(' revision %d object %d %d R %s%s',
[Change.RevisionIndex, Change.ObjectNumber, Change.Generation,
GetEnumName(TypeInfo(TPadesRevisionDecision), Ord(Change.Decision)),
ShadowTag[Change.IsAuthoritative]]));
end;
end;
finally
Pdf.Free;
end;
end;
Što se dogodi kad FieldMDP i anotacija dijele jedan objekt?
Kad /V polja i /Contents anotacije pokazuju na isti indirektni objekt, v3.126.2 drži FieldMDP bravu na snazi iako se promjena klasificira kao izmjena anotacije. Scenarij je lako sastaviti ručno: potpisnik zaključa polje Total s FieldMDP-om (ISO 32000-1 §12.8.2.4), a napadač natjera da /Contents tekstovne anotacije referencira isti string objekt koji drži vrijednost polja. Pod P=3 izmjena anotacije dopuštena je, pa je prije popravka prepisivanje tog dijeljenog stringa promijenilo zaključanu vrijednost polja s dopuštenom odlukom
Objekt sada nosi i ulogu anotacije i ulogu forme, a odluka anotacije ponovno provjerava stranu forme svaki put kad potpis ima FieldMDP transformaciju:
- Pod P=2 promjena anotacije odbija se odmah, točno kao i prije
- S FieldMDP
All, svako je polje zaključano, pa je dijeljena promjenaprdDisallowed - S FieldMDP
IncludeiliExclude, analyzer dijeljeni skalar ne može vratiti do jednog imena polja, pa je odlukaprdIndeterminateumjesto pogađanja - Bez FieldMDP-a primjenjuje se P=3 pravilo anotacije i promjena ostaje dopuštena
Jedan detalj izvještavanja bitan je za kapijski kod. Dijeljeni slučaj izvještava se kao Kind = prckAnnotation s Decision = prdIndeterminate, a prrFieldMdpUnresolved u skup rizika dodaje se samo za promjene klasificirane kao polja forme. Kapija koja traži prrFieldMdpUnresolved a ignorira Status promašuje ovaj slučaj u potpunosti
Kako Delphi kod treba raditi fail closed na analizi revizija?
Delphi kod treba prihvatiti potpisani dokument samo kad je status analize prasNoLaterChanges ili prasAllowed i nema strukturalnog rizika, a prasIndeterminate i prasSuspicious treba tretirati kao nepovjerljive, ne kao upozorenja za logirati i pustiti. Indeterminate znači da analyzer nije mogao dokazati da su kasnije revizije bile dopuštene; za napadača je unos koji pouzdano proizvodi Indeterminate jednako koristan kao onaj koji proizvodi Allowed ako ga vaš kod pusti. Globalni AnalyzePadesSignatureRevisions prima bilo koji TStream i čita ga od pozicije 0, što odgovara upload handlerima kojima nikad ne treba renderirati dokument
uses
Classes, SysUtils, TypInfo, FPdfPades;
const
// Duplikatne definicije bilježe se bez spuštanja Statusa
BlockingRisks: TPadesRevisionRisks = [prrMalformedRevisionChain,
prrDuplicateObjectDefinition, prrUnreferencedObjectDefinition,
prrSignatureObjectRedefined, prrCompressedObjectUnresolved,
prrResourceLimitExceeded];
function SignedRevisionsAcceptable(const FileName: string;
out Reason: string): Boolean;
var
Source: TFileStream;
Report: TPadesRevisionAnalysisReport;
begin
Result := False;
Reason := '';
Source := TFileStream.Create(FileName, fmOpenRead or fmShareDenyWrite);
try
Report := AnalyzePadesSignatureRevisions(Source);
finally
Source.Free;
end;
if Report.SignatureCount = 0 then
begin
Reason := 'no signature anchors the analysis';
Exit;
end;
if Report.Risks * BlockingRisks <> [] then
begin
Reason := 'structural risk in the revision chain';
Exit;
end;
case Report.Status of
prasNoLaterChanges, prasAllowed:
Result := True;
else
// prasIndeterminate i prasSuspicious odbijenice su, ne upozorenja
Reason := 'revision status ' +
GetEnumName(TypeInfo(TPadesRevisionAnalysisStatus), Ord(Report.Status));
end;
end;
Dvije granice vrijedi izreći ravnim jezikom. TPadesRevisionAnalysisReport ne govori ništa o CMS cjelovitosti ni o vjerodostojnosti certifikata, pa ova kapija stoji uz kriptografsku i trust validaciju, ne umjesto njih. A ispravan graf vlasništva ne čini P=3 sigurnim za svaki tijek rada. P=3 stvarno dopušta anotacije, i anotacija s neprozirnim appearanceom može prekriti potpisani tekst a da ne dotakne nijedan tok sadržaja. Ako su vaši certificirani dokumenti ugovori, a ne primjerci za pregled, ili certificirajte s P=2 ili usmjerite dopuštene promjene anotacija na čovjeka, kao u ovom helperu:
function AllowedAnnotationEditsUnderP3(
const Report: TPadesRevisionAnalysisReport): Integer;
var
i, j: Integer;
begin
Result := 0;
for i := 0 to High(Report.Signatures) do
if Report.Signatures[i].DocMdpPermission = 3 then
for j := 0 to High(Report.Signatures[i].Changes) do
if (Report.Signatures[i].Changes[j].Kind = prckAnnotation) and
(Report.Signatures[i].Changes[j].Decision = prdAllowed) then
Inc(Result);
end;
Kontrolni popis za audit revizija potpisa
Koristite ovaj popis da provjerite je li vaš verifikacijski pipeline bio izložen i radi li sada fail closed:
- Buildovi PDFium Component prije v3.126.2 mogli su izvještavati
prasAllowedza izmjene sadržaja stranice u DocMDP P=3 dokumentima; ponovno pokreniteTPdf.AnalyzeSignatureRevisionsnad certificiranim P=3 datotekama koje su prihvatili stariji buildovi - Ponovno provjerite P=3 dokumente s FieldMDP bravama gdje vrijednost polja i anotacija možda dijele indirektni objekt
- Prihvaćajte samo
prasNoLaterChangesiprasAllowed; tretirajteprasIndeterminateiprasSuspiciouskao nepovjerljive - Testirajte
Report.Riskskao iReport.Status, jerprrDuplicateObjectDefinitionsam po sebi ne mijenja status - Ne čitajte prazno polje
Changeskao čist rezultat kad je status potpisa Indeterminate; pala izgradnja uloga ne izvještava promjene - Ne oslanjajte se samo na
prrFieldMdpUnresolvedza hvatanje FieldMDP problema, jer se dijeljeni slučaj anotacije pokazuje samo kroz odluku i status - Odlučite trebaju li dopuštene promjene anotacija pod P=3 ljudski pregled u vašem tijeku rada
- Analizirajte izvorne bajtove datoteke; dokument prereisan s
SaveAsviše ne sadrži lanac revizija
Analiza revizija jedan je sloj provjere potpisa. Uporedite je s pregledom PDF digitalnih potpisa i PAdES razina za rječnik i baseline razinu, te s širom PDF auditom sigurnosnih rizika za JavaScript, launch akcije i ugrađene datoteke. TPdf.AnalyzeSignatureRevisions, AnalyzePadesSignatureRevisions i graf uloga svjestan vlasništva opisan ovdje isporučuju se u PDFium Component za Delphi, C++Builder i Lazarus