V zostaveniach PDFium Component pred v3.126.2 mohol TPdf.AnalyzeSignatureRevisions ohodnotiť skutočnú úpravu obsahu strany ako povolenú zmenu anotácie pod DocMDP P=3, lebo jeho graf rolí revízií bral spätnú referenciu /P podpisového widgetu na svoju stranu ako vlastníctvo. Od v3.126.2 oddeľuje PDFium Component navigačné hrany od hrán vlastneného payloadu, takže obsah strany ostáva obsahom strany. Bug report za touto opravou vyzerá na papieri neškodne. Certifikovaná zmluva povoľuje anotácie, druhá strana pridá jedno inkrementálne uloženie a analyzátor tvrdí, že každá neskoršia zmena je povolená. Potom niekto dá rozdiel medzi vykreslenými stranami a suma platby na strane 2 je iná
Tento článok je pokračovaním očami útočníka k prehľadu analýzy zmien revízií po podpise, takže vynecháva základy obnovy revízií a ohodnocovania DocMDP a ide priamo na graf objektov: ako bolo vlastníctvo modelované, prečo smer hrany rozhoduje o bezpečnostnom verdikte, čo sa zmenilo vo v3.126.2 a ako audítovať vlastnú akceptačnú logiku
Prečo prešla úprava strany ako zmena anotácie pod DocMDP P=3?
Úprava strany prešla, lebo starý graf rolí nasledoval každú nepriamu referenciu v slovníku, ako keby referencovaný objekt patril referujúcemu, a podpisový widget ukazuje späť na svoju stranu. Anotačný slovník nesie /P, nepriamu referenciu na objekt strany, na ktorej sedí (ISO 32000-1 §12.5.2). Táto položka je navigačná návest. Widget nevlastní stranu; strana vlastní widget cez svoje pole /Annots
Analyzátor priradí každému objektu sadu bitov rolí, než ohodnotí neskoršie zmeny: strana, anotácia, formulár a validačný materiál. Koreňové objekty dostanú rolu z vlastného slovníka a rola sa potom rozšíri na všetko, na čo referujú. V starej propagácii išla reťaz takto:
- Podpisový widget je
/Subtype /Widgets/FT /Sig, takže dostane rolu anotácie /Pwidgetu natlačí rolu anotácie na slovník strany, ktorý už má rolu strany- Strana natlačí obe role do
/Contents,/Resourcesa cez/Parentnahor do stromu Pages a nabok na každú sesterskú stranu - Slovník content streamu ako
<< /Length 812 >>nemá/Type, takže klasifikátor spadol späť na bity rolí a skontroloval rolu anotácie pred rolou strany
Pozmenený content stream preto vyšiel ako prckAnnotation. Podľa ISO 32000-1 §12.8.2.2 povoľuje DocMDP P=3 zmeny anotácií, takže rozhodnutie bolo prdAllowed a report sa zroloval do prasAllowed. Ten istý súbor pod P=2 bol odmietnutý, ale len náhodou: P=2 zakazuje zmeny anotácií, takže zle označený stream bol odmietnutý zo zlého dôvodu. Fixná propagácia v štyroch priechodoch pridala druhú slabosť. Payload doručený cez nepriame pole alebo cez dlhý reťaz, ktorého čísla objektov bežia vzad, možno nikdy nedostal žiadnu rolu
Prečo sa musí validátor podpisu pýtať, kto vlastní objekt?
Validátor podpisu sa musí pýtať, kto vlastní objekt, pretože inkrementálne aktualizácie PDF (ISO 32000-1 §7.5.6) dovoľujú komukoľvek pripojiť revíziu, ktorá predefinuje existujúce číslo objektu, a predefinované telo neoznáma, čím je. Podpis stále verifikuje, keďže pokrýva len bajty vlastnej revízie. Každá obrana proti manipulácii po podpise preto závisí od namapovania každého zmeneného objektu na štruktúru, ktorá ho používa, a potom od otázky, či podpisovateľ dovolil tej štruktúre sa zmeniť
Niekoľko publikovaných tried útokov pracuje presne v tej medzere. Útoky inkrementálnym ukladaním pripoja revíziu, ktorá vymení obsah strany, a spoliehajú sa na verifikátor kontrolujúci len podpísaný bajtový rozsah. Shadow útoky zasadia skrytý obsah pred podpisom a po podpise ho aktivujú malou, nevinnou vyzerajúcou zmenou. Útoky na certifikované dokumenty zneužívajú fakt, že P=2 a P=3 výslovne povoľujú niektoré neskoršie úpravy, a potom oblejú zakázanú úpravu do plášťa povolenej. Verifikátor, ktorý klasifikuje objekty podľa návestí ako /Type /Annot alebo podľa ľubovoľnej referenčnej cesty, ktorá k nim náhodou dôjde, je vystavený tretej triede: útočník potrebuje len jednu povolenú štruktúru, ktorá dosiahne tú zakázanú
Preto otázka nie je, ktoré objekty sa zmenili, ale kto ich vlastní. Content stream dosiahnutý zo strany cez /Contents je obsah strany bez ohľadu na to, čo všetko naň ešte ukazuje. Anotácia ukazujúca späť na stranu cez /P hovorí, kde anotácia býva, nie čo vlastní
Ako modeluje PDFium Component v3.126.2 vlastníctvo?
PDFium Component v3.126.2 berie spätné referencie ako navigáciu, drží ich mimo propagácie rolí a rozhoduje, ktoré kľúče sa počítajú ako navigácia, podľa štruktúrnej role slovníka, ktorý ich drží, nie podľa samotného mena kľúča. Tabuľka sumarizuje navigačné kľúče, ktoré už vlastníctvo nenesú
| Vlastniaci slovník | Kľúče brané ako navigácia | Referencia špecifikácie |
|---|---|---|
| Strana alebo uzol Pages | /Parent, /Kids, /Annots | ISO 32000-1 §7.7.3 |
| Anotácia alebo widget | /P | ISO 32000-1 §12.5.2 |
| Slovník widgetu alebo poľa | /Parent | ISO 32000-1 §12.7.3 |
Globálne filtrovanie podľa mena kľúča by vyrobilo novú dieru. Zdroj písma alebo XObject môže legitímne niesť meno /P, /Parent alebo /Annots a slovník /Resources, ktorý by vypustil svoju položku /P z propagácie, by útočníkovi dovolil skryť strane vlastnený XObject za nevinným menom zdroja. Vo v3.126.2 sa navigačný filter aplikuje len vtedy, keď vlastniaci slovník skutočne je strana, uzol Pages, anotácia, widget alebo pole. Ak jeden z týchto slovníkov nesie zdvojený navigačný kľúč, ako dve položky /P vo widgete, analyzátor nehádže, ktorú kópiu by použil prehliadač; stavba rolí zlyhá a podpis sa stane Indeterminate
Niekoľko ďalších pravidiel zatvára zostávajúce cesty preznačovania:
- Uzly Pages sú koreňmi roly strany samy o sebe, takže zdroje zdedené zo stromu Pages (ISO 32000-1 §7.7.3.4) vstupujú do kontextu strany cez skutočné vlastníctvo, nie cez prechádzanie
/Parentz detskej strany - Rola anotácie alebo formulára dorazivšia do katalógu, uzla Pages, strany, anotácie alebo slovníka poľa sa tam zastaví, lebo tieto štruktúrne objekty si stanovujú vlastné roly a prichádzajúca rola payloadu ich nesmie prebiť
- Rola strany je autoritatívna počas klasifikácie: objekt vlastnený stranou je
prckPageContent, aj keď neskoršia revízia ho prepíše sfalovaným/FT, návestím/Type /Annotalebo ho podelí s appearance streamom - Form XObject použitý len ako appearance poľa alebo anotácie si drží kategóriu formulára či anotácie, takže obyčajná regenerácia appearance po vyplnení formulára sa stále ohodnocuje pod normálnymi pravidlami povolení
- Widget bez vlastného
/FTrozlíši zdedený typ poľa cez reťaz/Parenta nerozlíšiteľný reťaz zlyhá pri stavbe rolí namiesto predvolenia anotácie - Bity rolí z každej neskoršej revízie sa zlúčia do rolí pokrytej revízie, takže neskoršia aktualizácia nemôže vymazať skorší vzťah vlastníctva strany tým, že najprv stream odpojí a potom ho upraví
Fixný bod namiesto fixného počtu priechodov
Dostupnosť rolí vo v3.126.2 beží ako pracovná fronta, ktorá iteruje, kým žiadny objekt nezíska nový bit roly, čo je pravý fixný bod bez ohľadu na hĺbku reťazca alebo číslovanie objektov. Nepriame polia ako pole /Contents uložené ako vlastný objekt sa prechádzajú tiež. Každý objekt môže získať nanajvýš štyri rôzne bity rolí, takže fronta je ohraničená na štyri položky na číslo objektu; jej prekročenie vyhodí prrResourceLimitExceeded. Referencia na voľný objekt, nezhoda generácie alebo pokazená hlavička objektu vyhodí prrMalformedRevisionChain a payload vo vnútri komprimovaného object streamu vyhodí prrCompressedObjectUnresolved. Každé z týchto zlyhaní končí prasIndeterminate, nikdy povoleným verdiktom, a keď zlyhanie nastane počas stavby rolí pokrytej revízie, podpis nenahlási žiadne Changes vôbec
Nasledujúca rutina vypíše úpravy obsahu strany, ktoré prežijú túto analýzu. Zmena prckPageContent sa nikdy neohodnotí ako prdAllowed: DocMDP P=1, 2 alebo 3 z nej spraví prdDisallowed a podpis bez DocMDP ju ohodnotí 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; // záznam, nič na uvoľnenie
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;
Čo sa stane, keď FieldMDP a anotácia zdieľajú jeden objekt?
Keď /V poľa a /Contents anotácie ukazujú na ten istý nepriamy objekt, drží v3.126.2 zámok FieldMDP v sile, aj keď je zmena klasifikovaná ako úprava anotácie. Scenár sa dá ľahko postaviť ručne: podpisovateľ zamkne pole Total cez FieldMDP (ISO 32000-1 §12.8.2.4) a útočník nechá /Contents textovej anotácie referovať ten istý reťazcový objekt, ktorý drží hodnotu poľa. Pod P=3 je úprava anotácie povolená, takže pred opravou prepísanie tohto zdieľaného reťazca zmenilo zamknutú hodnotu poľa s povoleným verdiktom
Objekt teraz nesie obe role, anotáciu aj formulár, a rozhodnutie o anotácii znovu skontroluje formulárovú stranu vždy, keď má podpis transformáciu FieldMDP:
- Pod P=2 je zmena anotácie odmietnutá rovno, presne ako predtým
- S FieldMDP
Allje zamknuté každé pole, takže zdieľaná zmena jeprdDisallowed - S FieldMDP
IncludealeboExcludenedokáže analyzátor vystopovať zdieľaný skalár späť k jednému menu poľa, takže rozhodnutie jeprdIndeterminatea nie odhad - Bez FieldMDP sa aplikuje pravidlo anotácie P=3 a zmena ostáva povolená
Jeden reportovací detail má význam pre gate kód. Zdieľaný prípad sa reportuje ako Kind = prckAnnotation s Decision = prdIndeterminate a prrFieldMdpUnresolved sa do množiny rizík pridáva len pri zmenách klasifikovaných ako formulárové polia. Gate, ktorý hľadá prrFieldMdpUnresolved a ignoruje Status, tento prípad úplne minie
Ako má Delphi kód zlyhávať bezpečne pri analýze revízií?
Delphi kód má prijať podpísaný dokument len vtedy, keď je stav analýzy prasNoLaterChanges alebo prasAllowed a nie je prítomné žiadne štruktúrne riziko, a prasIndeterminate a prasSuspicious má brať ako nedôveryhodné, nie ako varovania na zalogovanie a pustenie. Indeterminate znamená, že analyzátor nedokázal dokázať, že neskoršie revízie boli povolené; pre útočníka je vstup, ktorý spoľahlivo vyrába Indeterminate, rovnako užitočný ako taký, čo vyrába Allowed, ak ho váš kód pustí. Globálne AnalyzePadesSignatureRevisions berie ľubovoľný TStream a číta ho od pozície 0, čo sedí upload handlerom, ktoré nikdy nemusia dokument vykresliť
uses
Classes, SysUtils, TypInfo, FPdfPades;
const
// Zdvojené definície sa zaznamenajú bez zhoršenia Status
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 a prasSuspicious sú odmietnutia, nie varovania
Reason := 'revision status ' +
GetEnumName(TypeInfo(TPadesRevisionAnalysisStatus), Ord(Report.Status));
end;
end;
Dve hranice stoja za povedať nahlas. TPadesRevisionAnalysisReport nič nehovorí o integrite CMS ani o dôveryhodnosti certifikátov, takže táto brána sedí vedľa kryptografickej a trust validácie, nie na ich miesto. A správny graf vlastníctva nerobí P=3 bezpečným pre každý workflow. P=3 anotácie naozaj povoľuje a anotácia s nepriehľadným appearance môže zakryť podpísaný text bez toho, aby sa dotkla jediného content streamu. Ak sú vaše certifikované dokumenty zmluvy a nie kópie na recenziu, certifikujte buď s P=2 alebo púšťajte povolené zmeny anotácií na človeka, ako v tomto pomocníkovi:
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;
Kontrolný zoznam auditu revízií podpisu
Použite tento zoznam na kontrolu, či vaša verifikačná pipeline bola vystavená a či teraz zlyháva bezpečne:
- Zostavenia PDFium Component pred v3.126.2 mohli reportovať
prasAllowedpre úpravy obsahu strany v dokumentoch DocMDP P=3; pustiteTPdf.AnalyzeSignatureRevisionsznova na certifikované P=3 súbory, ktoré staršie zostavenia prijali - Znova skontrolujte dokumenty P=3 so zámkami FieldMDP, kde hodnota poľa a anotácia môžu zdieľať nepriamy objekt
- Prijímajte len
prasNoLaterChangesaprasAllowed;prasIndeterminateaprasSuspiciousberte ako nedôveryhodné - Testujte
Report.Risksako ajReport.Status, leboprrDuplicateObjectDefinitionnemení status sám o sebe - Nečítajte prázdne pole
Changesako čistý výsledok, keď je status podpisu Indeterminate; zlyhaná stavba rolí nenahlási žiadne zmeny - Nespoliehajte sa len na
prrFieldMdpUnresolvedpri odchytávaní problémov FieldMDP, keďže zdieľaný anotačný prípad vychádza na povrch len cez rozhodnutie a status - Rozhodnite, či povolené zmeny anotácií pod P=3 potrebujú vo vašom workflow ľudskú kontrolu
- Analyzujte pôvodné bajty súboru; dokument prepísaný cez
SaveAsuž reťaz revízií neobsahuje
Analýza revízií je jedna vrstva kontroly podpisu. Skombinujte ju s preskúmaním digitálnych podpisov PDF a úrovní PAdES pre slovníky a baseline úrovne a so širším audítom bezpečnostných rizík PDF pre JavaScript, launch akcie a vložené súbory. TPdf.AnalyzeSignatureRevisions, AnalyzePadesSignatureRevisions a graf rolí zohľadňujúci vlastníctvo popísaný tu prichádzajú v PDFium Component pre Delphi, C++Builder a Lazarus