Tehnični članak

PDFium Component DocMDP: widget /P in skrite spremembe

V gradnjah komponente PDFium Component pred v3.126.2 je TPdf.AnalyzeSignatureRevisions lahko pravo urejanje vsebine strani ocenil kot dovoljeno spremembo anotacije pod DocMDP P=3, ker je njegov graf vlog revizij povratno referenco /P podpisnega widgeta na njegovo stran obravnaval kot lastništvo. Od v3.126.2 komponenta PDFium Component loči navigacijske povezave od povezav lastniške vsebine, tako da vsebina strani ostane vsebina strani. Hroščje poročilo za tem popravkom je na papirju videti neškodljivo. Certificirana pogodba dovoljuje anotacije, nasprotna stranka doda en inkrementalni shranjevalni korak, analizator pa pove, da je vsaka poznejša sprememba dovoljena. Potem nekdo primerja izrisane strani in znesek plačila na strani 2 je drugačen

Ta članek je nadaljevanje s pogledom napadalca na pregled analize sprememb revizij po podpisu, zato izpušča osnove ponovne izgradnje revizij in ocenjevanja DocMDP ter gre naravnost k grafu objektov: kako je bilo lastništvo modelirano, zakaj smer povezave odloča varnostno sodbo, kaj se je spremenilo v v3.126.2 in kako preveriti lastno sprejemno logiko

Zakaj je urejanje strani pod DocMDP P=3 šlo skozi kot sprememba anotacije?

Urejanje strani je šlo skozi, ker je stari graf vlog sledil vsaki posredni referenci v slovarju, kot da objekt, na katerega se sklicuje, pripada tistemu, ki se sklicuje, podpisni widget pa kaže nazaj na svojo stran. Slovar anotacije nosi /P, posredno referenco na objekt strani, na katerem stoji (ISO 32000-1 §12.5.2). Ta vnos je navigacijski namig. Widget ne lasti strani; stran lasti widget prek svoje tabele /Annots

Analizator vsakemu objektu dodeli množico bitov vlog, preden oceni poznejše spremembe: stran, anotacija, obrazec in validacijsko gradivo. Korenski objekti dobijo svojo vlogo iz svojega slovarja, vloga pa se nato razleze na vse, na kar se sklicujejo. V stari propagaciji je veriga tekla takole:

  1. Podpisni widget je /Subtype /Widget s /FT /Sig, zato dobi vlogo anotacije
  2. Widgetov /P potisne vlogo anotacije na slovar strani, ki vlogo strani že ima
  3. Stran potisne obe vlogi v /Contents, /Resources in prek /Parent navzgor v drevo Pages ter čez na vsako sestrsko stran
  4. Slovar toka vsebine, kot je << /Length 812 >>, nima /Type, zato je razvrščevalnik padel nazaj na bite vlog in preveril vlogo anotacije pred vlogo strani
PDFium Component diagram grafa vlog DocMDP pred v3.126.2, kjer povratna referenca widgeta podpisa /P potisne vlogo anotacije na slovar strani, stran jo razleze prek /Contents na tok vsebine brez vnosa /Type, razvrščevalnik izpiše prckAnnotation, ocenjevanje P=3 pa vrne prdAllowed
Pred v3.126.2 je graf vlog vsako posredno referenco obravnaval kot lastništvo, zato je vnos widgeta /P potisnil vlogo anotacije na stran in pravo urejanje strani je analizator zapustilo kot dovoljeno spremembo anotacije

Spremenjeni tok vsebine je zato izšel kot prckAnnotation. Pod ISO 32000-1 §12.8.2.2 DocMDP P=3 dovoljuje spremembe anotacij, zato je bila odločitev prdAllowed in poročilo se je zvilo v prasAllowed. Isti zapis pod P=2 je bil zavrnjen, a samo po naključju: P=2 prepove spremembe anotacij, zato je bil napačno označeni tok zavrnjen iz napačnega razloga. Fiksirana propagacijska zanka s štirimi prehodi je dodala še drugo slabost. Obremenka, dosežena skozi posredno tabelo ali skozi dolgo verigo, katere številke objektov tečejo nazaj, morda sploh ne dobi nobene vloge

Zakaj mora validator podpisov vprašati, kdo lasti objekt?

Validator podpisov se mora vprašati, kdo lasti objekt, ker inkrementalne posodobitve PDF (ISO 32000-1 §7.5.6) komur koli dovolijo pripeti revizijo, ki redefinira obstoječo številko objekta, redefinirano telo pa ne naznani, kaj je. Podpis se še vedno preveri, ker pokriva samo bajte svoje revizije. Vsaka obramba pred manipulacijo po podpisu je zato odvisna od preslikave vsakega spremenjenega objekta na strukturo, ki ga uporablja, in od vprašanja, ali je podpisnik dovolil, da se ta struktura spremeni

Nekaj objavljenih razredov napadov ravno tam dela. Napadi z inkrementalnim shranjevanjem pripnejo revizijo, ki zamenja vsebino strani, in se zanašajo na to, da preverjevalnik preverja samo podpisani bajtni razpon. Senčni napadi posadijo skrito vsebino pred podpisom in jo aktivirajo pozneje z majhno, neškodljivo vidno spremembo. Napadi na certificirane dokumente zlorabijo to, da P=2 in P=3 izrecno dovoljujeta nekatere poznejše spremembe, prepovedano spremembo paoblečeta v dovoljeno. Preverjevalnik, ki razvršča objekte po oznakah, kot je /Type /Annot, ali po kateri koli referenčni poti, ki jih po naključju doseže, je izpostavljen tretjemu razredu: napadalec potrebuje samo eno dovoljeno strukturo, ki doseže prepovedano

Zato vprašanje ni, kateri objekti so se spremenili, ampak kdo jih lasti. Tok vsebine, dosežen iz strani prek /Contents, je vsebina strani, kar koli že kaže nanj. Anotacija, ki kaže nazaj na stran prek /P, pove, kje anotacija živi, ne pa kaj lasti

Kako komponenta PDFium Component v3.126.2 modelira lastništvo?

Komponenta PDFium Component v3.126.2 obravnava povratne reference kot navigacijo, jih drži izven propagacije vlog in odloča, kateri ključi štejejo kot navigacija, iz strukturne vloge slovarja, ki jih nosi, ne iz samega imena ključa. Tabela povzame navigacijske ključe, ki lastništva več ne nosijo

Lastniški slovarKljuči, obravnavani kot navigacijaSklic na specifikacijo
Stran ali vozlišče Pages/Parent, /Kids, /AnnotsISO 32000-1 §7.7.3
Anotacija ali widget/PISO 32000-1 §12.5.2
Slovar widgeta ali polja/ParentISO 32000-1 §12.7.3
PDFium Component v3.126.2 graf objektov, ki loči povezave lastniške vsebine, kot sta /Contents in /Annots, ki širita vlogi strani in anotacije, od navigacijskih povezav, kot je widget /P, ki ne nosita vlog, z navigacijskimi ključi na lastniški slovar in prckPageContent obdržanim na toku vsebine tudi pod ponarejenim /Type
v3.126.2 drži povratne reference izven propagacije vlog: vloge potujejo samo skozi pravo lastništvo, tok vsebine torej ostane vsebina strani, namig /P pa ne odloča nič

Filtriranje po imenu ključa globalno bi ustvarilo novo luknjo. Vira pisave ali XObject se lahko upravičeno imenuje /P, /Parent ali /Annots, slovar /Resources, ki bi iz propagacije odvrgel svoj vnos /P, pa bi napadalcu dovolil skriti objekt XObject, v lasti strani, za nedolžnim imenom vira. V v3.126.2 navigacijski filter velja samo, kadar je lastniški slovar dejansko stran, vozlišče Pages, anotacija, widget ali polje. Če ena od teh slovarjev nosi podvojen navigacijski ključ, na primer dva vnosa /P v widgetu, analizator ne ugane, katero kopijo bi uporabil pregledovalnik; gradnja vlog spodleti in podpis postane Indeterminate

Še nekaj pravil zapre preostale poti ponovnega označevanja:

  • Vozlišča Pages so koreni vloge strani po lastni pravici, zato viri, podedovani iz drevesa Pages (ISO 32000-1 §7.7.3.4), vstopijo v kontekst strani skozi pravo lastništvo, ne skozi sprehod po /Parent iz otroške strani
  • Vloga anotacije ali obrazca, ki prispe na katalog, vozlišče Pages, stran, anotacijo ali slovar polja, se tam ustavi, ker ti strukturni objekti ustanovijo svoje vloge in prihajajoča vloga obremenke jih ne sme preglasiti
  • Vloga strani je med razvrščanjem avtoritativna: objekt v lasti strani je prckPageContent, tudi če ga poznejša revizija prepiše s ponarejenim /FT, oznako /Type /Annot ali ga deli s tokom videza
  • Form XObject, uporabljen samo kot videz polja ali anotacije, obdrži svojo kategorijo obrazca ali anotacije, zato navadna ponovna izdelava videza po izpolnitvi obrazca ostane ocenjena pod običajnimi pravili dovoljenj
  • Widget brez lastnega /FT razreši podedovano vrsto polja skozi verigo /Parent, nerazrešljiva veriga pa spodleti gradnjo vlog namesto da bi privzela anotacijo
  • Biti vlog iz vsake poznejše revizije se združijo v vloge pokrite revizije, tako da poznejša posodobitev ne more izbrisati zgodnejšega razmerja lastništva strani tako, da tok najprej odcepi in ga nato uredi

Fiksna točka namesto fiksnega števila prehodov

Dosegljivost vlog v v3.126.2 teče kot delovna vrsta, ki se ponavlja, dokler noben objekt ne dobi novega bita vloge, kar je prava fiksna točka, ne glede na globino verige ali številčenje objektov. Posredne tabele, kot je tabela /Contents, shranjena kot lasten objekt, so prav tako prehojene. Vsak objekt lahko dobi največ štiri različne bite vlog, zato je vrsta omejena na štiri vnose na številko objekta; preseganje tega proračuna sproži prrResourceLimitExceeded. Referenca na prosti objekt, neujemanje generacije ali polomljena glava objekta sprožita prrMalformedRevisionChain, obremenka znotraj stisnjenega toka objektov pa prrCompressedObjectUnresolved. Vsak od teh spodletelih primerov se konča s prasIndeterminate, nikoli z dovoljeno sodbo, kadar pa spodletitev zgodi med gradnjo vlog pokrite revizije, podpis sploh ne poroča o Changes

Naslednja rutina našteje urejanja vsebine strani, ki preživijo to analizo. Sprememba prckPageContent se nikoli ne oceni prdAllowed: DocMDP P=1, 2 ali 3 jo naredi prdDisallowed, podpis brez DocMDP pa jo oceni 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, ničesar za sprostiti
    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;

Kaj se zgodi, kadar FieldMDP in anotacija delita en objekt?

Kadar /V polja in /Contents anotacije kažeta na isti posredni objekt, v3.126.2 obdrži zaklep FieldMDP v veljavi, tudi če je sprememba razvrščena kot urejanje anotacije. Scenarij se da ročno izdelati: podpisnik zaklene polje Total s FieldMDP (ISO 32000-1 §12.8.2.4), napadalec pa naredi, da se /Contents besedilne anotacije sklicuje na isti niz objektov, ki drži vrednost polja. Pod P=3 je urejanje anotacije dovoljeno, zato je prepis tega deljenega niza pred popravkom spremenil zaklenjeno vrednost polja z dovoljeno sodbo

Objekt zdaj nosi tako vlogo anotacije kot obrazca, odločitev anotacije pa ponovno preveri stran obrazca, kadar koli ima podpis transformacijo FieldMDP:

  • Pod P=2 je sprememba anotacije čisto zavrnjena, točno kot prej
  • S FieldMDP All je zaklenjeno vsako polje, zato je deljena sprememba prdDisallowed
  • S FieldMDP Include ali Exclude analizator deljenega skalarnega podatka ne more slediti nazaj do enega imena polja, zato je odločitev prdIndeterminate namesto ugibanja
  • Brez FieldMDP velja pravilo anotacije P=3 in sprememba ostane dovoljena
PDFium Component odločitveni diagram FieldMDP, kjer zaklenjeno polje Total /V in anotacija /Contents sklicujeta isti posredni objekt, z razvejanjem prek DocMDP P=2, FieldMDP All, FieldMDP Include ali Exclude ter brez FieldMDP na sodbe prdDisallowed, prdIndeterminate ali prdAllowed za isto deljeno urejanje
Ko en posredni objekt nosi tako vlogo anotacije kot obrazca, odločitev anotacije ponovno preveri zaklep FieldMDP, zato isto urejanje sega od dovoljenega prek zavrnjenega do nedoločenega

Ena podrobnost poročanja je pomembna za pregrado v kodi. Deljeni primer se poroča kot Kind = prckAnnotation z Decision = prdIndeterminate, prrFieldMdpUnresolved pa se v množico tveganj doda samo za spremembe, razvrščene kot polja obrazcev. Pregrada, ki išče prrFieldMdpUnresolved in ignorira Status, ta primer spregleda popolnoma

Kako naj koda v Delphiju ob analizi revizij spodleti zaprto?

Koda v Delphiju naj sprejme podpisan dokument samo, kadar je stanje analize prasNoLaterChanges ali prasAllowed in ni nobenega strukturnega tveganja, prasIndeterminate in prasSuspicious pa naj obravnava kot nezaupanja vredna, ne kot opozorili za dnevnik in prehod. Nedoločeno pomeni, da analizator ni mogel dokazati, da so bile poznejše revizije dovoljene; za napadalca je vnos, ki zanesljivo izda Nedoločeno, enako koristen kot tisti, ki izda Dovoljeno, če ga vaša koda spusti skozi. Globalni AnalyzePadesSignatureRevisions sprejme vsak TStream in ga bere od položaja 0, kar ustreza ročajevalnikom nalaganj, ki dokumenta ne potrebujejo izrisovati

uses
  Classes, SysUtils, TypInfo, FPdfPades;

const
  // Podvojeni definicije se zapišejo brez znižanja 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 in prasSuspicious sta zavrnitvi, ne opozorili
    Reason := 'revision status ' +
      GetEnumName(TypeInfo(TPadesRevisionAnalysisStatus), Ord(Report.Status));
  end;
end;

Dve meji sta vredni jasne izjave. TPadesRevisionAnalysisReport ne pove nič o celovitosti CMS ali zaupanju v certifikat, zato ta pregrada stoji ob kriptografski in zaupnostni validaciji, ne namesto njiju. In pravi graf lastništva ne naredi P=3 varnega za vsak delovni tok. P=3 res dovoljuje anotacije, anotacija z motnim videzom pa lahko pokrije podpisano besedilo, ne da bi se dotaknila enega samega toka vsebine. Če so vaši certificirani dokumenti pogodbe in ne pregledne kopije, bodisi certificirajte s P=2 bodisi dovoljene spremembe anotacij usmerite človeku, kot v tem pomožniku:

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 seznam za revizijo podpisnih revizij

Ta seznam uporabite, da preverite, ali je bila vaša veriga preverjanja izpostavljena in ali zdaj spodleti zaprto:

  • Gradnje komponente PDFium Component pred v3.126.2 so lahko poročale prasAllowed za urejanja vsebine strani v dokumentih DocMDP P=3; ponovno poženite TPdf.AnalyzeSignatureRevisions nad certificiranimi datotekami P=3, sprejetimi s strani starejših gradnji
  • Ponovno preverite dokumente P=3 z zaklepi FieldMDP, kjer se vrednost polja in anotacija morda delita posredni objekt
  • Sprejmite samo prasNoLaterChanges in prasAllowed; prasIndeterminate in prasSuspicious obravnavajte kot nezaupanja vredni
  • Preizkusite Report.Risks kot tudi Report.Status, ker prrDuplicateObjectDefinition stanja sam po sebi ne spremeni
  • Prazne tabele Changes ne berite kot čistega rezultata, kadar je stanje podpisa Nedoločeno; spodletela gradnja vlog ne poroča o nobeni spremembi
  • Ne zanašajte se samo na prrFieldMdpUnresolved, da ujamete težave FieldMDP, saj deljeni primer anotacije pride na površje samo skozi odločitev in stanje
  • Odločite, ali dovoljene spremembe anotacij pod P=3 potrebujejo človeški pregled v vašem delovnem toku
  • Analizirajte izvirne bajte datoteke; dokument, prepisan s SaveAs, verige revizij več ne vsebuje

Analiza revizij je ena plast preverjanja podpisa. Spara jo z pregledom digitalnih podpisov PDF in ravni PAdES za slovar in osnovno raven ter z širšim revidiranjem varnostnih tveganj PDF za JavaScript, akcije zagona in vgrajene datoteke. TPdf.AnalyzeSignatureRevisions, AnalyzePadesSignatureRevisions in graf vlog, ki pozna lastništvo, opisan tukaj, prihajajo v komponenti PDFium Component za Delphi, C++Builder in Lazarus