Tehnični članak

Izolacija kodekov slik PDF v HotPDF v delovnih procesih

HotPDF zna dekodirati tri najbolj tvegane filtre slik PDF, DCTDecode, JPXDecode in JBIG2Decode, znotraj ločenega kratkotrajnega delovnega procesa namesto znotraj vaše aplikacije. Lastnost, ki to omogoči, je CodecIsolationMode, praktični učinek pa je, da napačno oblikovan kodni tok JPEG 2000, ki bi prej sesul vašo aplikacijo VCL, zdaj ubije zavržljiv podrejen proces, medtem ko gostitelj sporoči statusno kodo in nadaljuje

Ta razlika je najpomembnejša tam, od koder PDF-ji dejansko prihajajo: obrazec za nalaganje, poštni prehod, skenirna naprava, partnerski FTP predal. Teh bajtov ne nadzorujete vi, kodeki slik pa so mesto, kjer živi zgodovinska škoda

Zakaj ena slaba slika podre celotno aplikacijo?

Ker je kodek slike tisti del bralnika PDF, ki nad podatki, ki jih nadzoruje napadalec, poganja kompleksen stroj stanj skoraj brez strukturnih preverjanj, na katera bi se lahko naslonil. Ko bajti dosežejo dekodirnik JPEG 2000 ali JBIG2, je tabela navzkrižnih sklicev že razčlenjena, objekt razrešen, veriga filtrov odvita, tisto, kar ostane, pa je surov kodni tok, ki pove, koliko ploščic, koliko komponent, koliko bitov na vzorec. Napačno število tam ni napaka razčlenjevanja. Je napačna velikost dodelitve ali indeks izven obsega znotraj tesne dekodirne zanke

Omejitve proračuna pomagajo in bi jih morali že imeti. HotPDF omeji širjenje z DecodeBudgetBytes in DocumentDecodeBudgetBytes, verige filtrov pa omeji z DecodeFilterLimit in DecodePipelineDepthLimit; razlogi za te omejitve so obravnavani v omejenem dekodiranju za gnezdene filtre in bombe PDF. Toda proračun bajtov odgovori le na eno vprašanje, koliko izhoda je dovoljeno. Ne more odgovoriti, kaj se zgodi, ko dekodirnik odpove, še preden ustvari kakršen koli izhod. Kršitev dostopa znotraj dekodirne zanke ni kršitev politike, ki bi jo lahko zavrnili; je dogodek na ravni procesa, edino zanesljivo zadrževanje dogodka na ravni procesa pa je drug proces

Kaj HotPDF izolira, in česa ne

HotPDF izolira natanko tri vrste kodekov, naštete kot hckDCT, hckJPX in hckJBIG2 v enoti HPDFCodecIsolation. Vse ostalo, Flate, LZW, RunLength, ASCII85, CCITT, ostane znotraj procesa, ker so ti dekodirniki dovolj preprosti, da jih omejite s proračuni, in niso mesto, od koder prihajajo zanimive napake

Prenosna plast je namerno ozka. Gostitelj dodeli eno omejeno preslikavo v skupnem pomnilniku, vanjo zapiše fiksno glavo THPDFCodecSharedHeader, stisnjen vhod in morebitne globalne segmente JBIG2, zažene delovni proces in počaka. Delovni proces nazaj v isto preslikavo zapiše dekodirane slikovne pike in nastavi statusno besedo. Ni nobenega protokola cevi, ki bi se lahko razsinhroniziral, ni serializacijske oblike za fuzzanje, glava pa nosi magično vrednost in različico, tako da je neujemajoč delovni binarni program zavrnjen namesto napačno prebran

uses
  HPDFDoc, HPDFCodecIsolation;

var
  Pdf: THotPDF;
  Info: THPDFCodecWorkerInfo;
  Bmp: TBitmap;
begin
  Pdf := THotPDF.Create(nil);
  try
    // Odpovej zaprto: teh kodekov nikoli ne dekodiraj znotraj procesa
    Pdf.CodecIsolationMode := cimRequired;
    Pdf.CodecWorkerExecutable := 'HotPDFCodecWorker.exe';
    Pdf.CodecWorkerTimeoutMilliseconds := 5000;       // 1..600000
    Pdf.CodecWorkerMemoryLimitBytes := 268435456;     // 0 ali >= 64 MiB
    Pdf.DecodeBudgetBytes := 134217728;

    if Pdf.LoadFromFile('untrusted-upload.pdf') = 1 then
      if Pdf.GetLoadedImageCount > 0 then
      begin
        Bmp := Pdf.ExtractLoadedImage(0);
        try
          if Pdf.GetLastCodecWorkerInfo(Info) then
            LogCodecOutcome(Info);
        finally
          Bmp.Free;
        end;
      end;
  finally
    Pdf.Free;
  end;
end;

Pustite CodecWorkerExecutable prazno in HotPDF razreši delovni proces poleg vaše lastne izvedljive datoteke, kot HotPDFCodecWorker.exe v imeniku ParamStr(0). Nastavite ga izrecno, kadar vaša namestitev postavi delovni proces drugam; vrednost se razširi prek ExpandFileName, zato se relativna pot razreši glede na trenutni imenik in ne glede na imenik aplikacije, kar na storitvi redko koristi

Samodejno ali zahtevano: katero napako raje izberete?

Tri vrednosti THPDFCodecIsolationMode kodirajo tri različne odgovore na eno vprašanje: kaj naj se zgodi, ko delovni proces sploh ne more teči. cimDisabled v celoti preskoči izolacijo in dekodira znotraj procesa, kar je vedenje pred različico 3.x. cimAutomatic, privzeta vrednost, poskusi z delovnim procesom in tiho pade nazaj na dekodiranje znotraj procesa, kadar delovni program manjka ali se ne zažene, kar je sporočeno kot status cwsUnavailable. cimRequired zavrne ta padec nazaj: nedosegljiv delovni proces označi dekodiranje kot obravnavano in neuspešno, tako da noben nezaupljiv kodni tok nikoli ne doseže vašega naslovnega prostora

Izbirajte glede na model groženj in ne glede na udobje. Namizni pregledovalnik, ki odpira dokumente, ki jih uporabnik že ima na disku, je v redu s cimAutomatic, kjer manjkajoč delovni proces poslabša delovanje na klasično vedenje namesto da bi pokvaril izdelek. Storitev za sprejem, ki razčlenjuje datoteke iz interneta, naj poganja cimRequired, ker je napaka namestitve, ki tiho odstrani plast izolacije, natanko vrsta regresije, ki je nihče ne opazi, dokler ni pomembna. Opazite asimetrijo: le cwsUnavailable sproži padec nazaj. Delovni proces, ki se je zagnal in nato sesul, potekel ali dosegel omejitev, je napaka dekodiranja v obeh načinih, nikoli tih ponoven poskus znotraj procesa

Branje razsodbe iz THPDFCodecWorkerStatus

GetLastCodecWorkerInfo vrne izid zadnjega izoliranega dekodiranja, statusna enumeracija pa je dovolj natančna, da poganja resnične operativne odločitve namesto generičnega dnevniškega zapisa "slika ni uspela". Vrednosti so cwsNotRun, cwsSucceeded, cwsUnavailable, cwsLaunchFailed, cwsTimedOut, cwsCrashed, cwsDecodeFailed, cwsProtocolError in cwsOutputLimit

Obravnavajte jih kot tri skupine. Težave namestitve so cwsUnavailable in cwsLaunchFailed: nekdo je izdal brez delovnega procesa ali pa protivirusni izdelek blokira ustvarjanje procesov. Težave dokumenta so cwsDecodeFailed in cwsOutputLimit: datoteka je napačno oblikovana ali večja, kot dovoljuje vaša politika, njena zavrnitev pa je pravilen odgovor. Zanimiva skupina je cwsTimedOut in cwsCrashed, ker sta to dogodka, ki bi prej obesila ali ubila gostiteljski proces. Ko se to zgodi, vam spremljajoča polja ProcessId, ExitCode in ElapsedMilliseconds dajo dovolj, da povežete z zapisom Windows Error Reporting in se odločite, ali je ena datoteka stranke patološka ali vas nekdo preizkuša

procedure LogCodecOutcome(const Info: THPDFCodecWorkerInfo);
begin
  case Info.Status of
    cwsSucceeded:
      ; // ni česa poročati
    cwsUnavailable, cwsLaunchFailed:
      Alert('Codec worker not deployed: ' + Info.ErrorMessage);
    cwsTimedOut, cwsCrashed:
      Quarantine(Format('pid %d exit %d after %d ms',
        [Info.ProcessId, Info.ExitCode, Info.ElapsedMilliseconds]));
  else
    RejectDocument(Info.ErrorMessage);
  end;
end;

Omejitve, ki dejansko zavezujejo

Za vsako izolirano dekodiranje veljajo tri ločene zgornje meje, poznavanje, katera je sprožila napako, pa prihrani popoldne ugibanja. CodecWorkerTimeoutMilliseconds je privzeto 10.000 in je preverjen v obseg od 1 do 600.000; vrednost izven tega obsega sproži izjemo namesto tihega obrezovanja. CodecWorkerMemoryLimitBytes je privzeto 536.870.912 bajtov in mora biti bodisi nič, kar pomeni brez omejitve, bodisi vsaj 67.108.864 bajtov, ker manjša omejitev ne more zadržati realistične delovne množice dekodirnika in bi zavrnila vsak dokument. Omejitev pomnilnika izvaja objekt Windows Job Object s semantiko uničenja ob zaprtju, tako da delovni proces umre skupaj z opravilom, tudi če je gostitelj nenadoma prekinjen

Tretja zgornja meja je izhodna omejitev in je izpeljana, ne nastavljena. HotPDF izračuna zahtevane bajte iz zahtevanega območja ali iz pričakovane geometrije slike, kot širina krat višina krat tri za 24-bitni izhod, nato pa to vrednost obreže navzdol na DecodeBudgetBytes, kadar je proračun nastavljen. Dekodirnik, ki poroča verjetno glavo in nato poskuša izdati veliko več slikovnih pik, kot jih geometrija dovoljuje, je ustavljen s samo preslikavo, gostitelj pa vidi cwsOutputLimit. Zato sta plast izolacije in proračun dekodiranja dopolnili: proračun določa, kako velika sme biti slika, meja izolacije pa poskrbi, da laž o tej velikosti ne more postati zapis izven meja v vašem procesu

Kam to sodi v utrjeni poti sprejema

Izolacija procesov je najbolj zunanja plast verige obrambe, ki se začne veliko prej. Strukturne omejitve zavrnejo neverjetne dokumente ob razčlenjevanju. Proračuni filtrov omejijo širjenje. Izolacija zadrži tisto, kar preživi oboje. Za dokumente, ki dosežejo plast slik, se splača vedeti, kateri kodek dejansko izvajate, saj imata obravnava JPXDecode in simbolni slovarji JBIG2 zelo različna profila napak, JBIG2 pa še posebej nosi globalne segmente med stranmi, ki bi jih naiven peskovnik na posamezno sliko pokvaril

Cena je poštena in vredna povedati: zagon procesa na izolirano sliko doda milisekunde, dokument s stotinami skeniranih strani pa bo to čutil. Merite jo glede na to, kaj kupi. Na paketnem pretvorniku, ki teče brez nadzora čez noč, je izguba prepustnosti neopazna, zadrževanje sesutja pa je vse, kar šteje. Na interaktivnem pregledovalniku, ki odpira dokumente, ki jim uporabnik že zaupa, je cimDisabled ali cimAutomatic razumna privzeta vrednost. Način je navadna lastnost, zato vas nič ne ovira, da za vsak razred dokumentov izbirate med izvajanjem

HotPDF dostavlja plast izolacije, proračune dekodiranja in strukturne omejitve razčlenjevalnika kot eno izvorno komponento VCL za Delphi in C++Builder, brez zunanjega izvajalnega okolja poleg same delovne izvedljive datoteke. Celotna dokumentacija API in preizkusna gradnja sta na strani izdelka HotPDF