Technical Article

Revizija šifrovanja i dozvola PDF-a u Delphi-ju pomoću PDFlibPas-a

Fleg dozvole nije sigurnosni mehanizam. Bit koji kaže „zabranjeno kopiranje” živi unutar istog /Encrypt rečnika kao i sama kriptografija, što mu daje privid sprovođenja koji zapravo nema, a onog trenutka kada te dve stvari tretirate kao jednu, vaša revizija počinje da daje pogrešne odgovore. Jedino pitanje koje vredi postaviti za PDF nije „da li je šifrovan”. Ono je specifičnije i teže: koji algoritam, koja revizija bezbednosnog rukovaoca (security handler), koja od dve lozinke je postavljena, koji bitovi dozvola su navedeni i koje delove datoteke šifrovanje zapravo dotiče. Datoteka može biti formalno šifrovana, a praktično otvorena. Može odbiti čitanje, a ipak ostaviti svoje metapodatke u čistom tekstu (plaintext). Može zaključati štampanje pomoću flega koji je svaki pregledač slobodan da ignoriše. Revizija PDF-a znači rešavanje svih ovih pitanja pojedinačno, a PDFlibPas, losLab PDF motor za Delphi i C++Builder, izlaže svako od njih kako kroz ravan API sa celobrojnim opisnicima (handles), tako i kroz tipizirani sloj klasa.

Šta zapravo beleži /Encrypt rečnik

ISO 32000-1 §7.6 definiše bezbednost dokumenta kroz nekoliko unosa u rečniku, a PDFlibPas ih preslikava jedan na jedan u TPDFEncryption rekordu. Verzija filtera V i revizija R biraju porodicu algoritama. Length nosi veličinu ključa. Bitovi dozvole se nalaze u P, stringovi za validaciju vlasničke (owner) i korisničke (user) lozinke u O i U (uz OE i UE dodate za AES-256), fleg EncryptMetadata ide uporedo, dok tri dodatna polja imenuju filtere šifrovanja (crypt filters) koji se primenjuju na stringove, tokove (streams) i ugrađene datoteke respektivno.

Vrednost ovog rekorda je u tome što on ništa ne interpretira umesto vas. Vraća sirovi rečnik i prepušta vam da sami izvedete zaključke, što je upravo ono što je potrebno za reviziju. Slučaj čistog teksta unutar šifrovanog dokumenta se prikazuje u StringFilterIdentity i StreamFilterIdentity: kada je bilo koji od njih tačan (true), odgovarajući podaci prolaze kroz Identity filter netaknuti, bez obzira na to šta prijavljuje status šifrovanja dokumenta. Skener koji se zaustavlja na tome da je „rečnik /Encrypt prisutan” proglasiće takvu datoteku zaštićenom iako su njeni stringovi i tokovi u čistom tekstu. Ista nijansa važi i za metapodatke. Kada je EncryptMetadata netačan (false), XMP paket ostaje čitljiv za bilo koji indeksator dok sadržaj stranice nije, što je važno znati onog trenutka kada se vaša pravila usmeravanja oslanjaju na polje naslova ili autora.

Kratka provera bezbednosti pomoću ravnog API-ja

Za većinu procesa obrade, četiri ravna poziva daju odgovore na svakodnevna pitanja. Funkcija LoadFromFile ova vraća 1 u slučaju uspeha, a nakon što se dokument otvori, inspektori šifrovanja izveštavaju o njegovom dešifrovanom stanju:

var
  PDF: TPDFlib;
begin
  PDF := TPDFlib.Create;
  try
    if PDF.LoadFromFile('contract.pdf', UserPassword) <> 1 then
      raise Exception.Create('Open failed: wrong password or damaged file');
    Writeln('status    : ', PDF.EncryptionStatus);     // decrypted / encrypted / unknown
    Writeln('algorithm : ', PDF.EncryptionAlgorithm);  // RC4 vs AES family
    Writeln('strength  : ', PDF.EncryptionStrength);   // key length class
    Writeln('owner pw? : ', PDF.CheckPassword(CandidatePassword));
  finally
    PDF.Free;
  end;
end;

CheckPassword je važniji nego što sugeriše njegov jednostavan potpis. PDF definiše dve lozinke sa nejednakim pravima. Korisnička lozinka je neophodna da bi se datoteka uopšte otvorila. Vlasnička lozinka daje puna prava i zaobilazi svaki bit dozvole. Bajtovi na disku su identični u oba slučaja, ali sesija otvorena pod vlasničkom lozinkom može da uradi stvari koje sesija pod korisničkom lozinkom ne može, tako da revizija koja ne beleži koje su akreditivi predstavljeni zapravo beleži samo pola istine. Sloj klasa omogućava ispitivanje ove razlike. TPDFDocument.HasUserPassword i HasOwnerPassword prijavljuju šta datoteka zahteva, dok IsUserPassword i IsOwnerPassword prijavljuju koja lozinka je zapravo otvorila trenutnu sesiju. Zabeležite tu činjenicu. Nikada nemojte beležiti same vrednosti lozinki.

Lestvica jačine, gde „AES-256” označava dve različite stvari

Ravne funkcije Encrypt i EncryptFile primaju celobrojni parametar Strength sa pet značenjskih vrednosti: 0 za 40-bitni RC4, 1 za 128-bitni RC4, 2 za 128-bitni AES čitljiv od verzije Acrobat 7, 3 za 256-bitni AES predstavljen u verziji Acrobat 9, i 4 za 256-bitni AES koji zahteva Acrobat X i noviji.

Zanimljivo je da su i 3 i 4 označeni kao AES-256, ali to nisu isti sistemi. Jačina 3 se mapira na reviziju bezbednosnog rukovaoca 5, prelazno rešenje koje je isporučeno uz Acrobat 9 i koje ISO nikada nije usvojio. Jačina 4 se mapira na reviziju 6, čija je funkcija izvođenja ključa ojačana i standardizovana u ISO 32000-2. Za dokument koji kreirate danas nema razloga da izaberete 3 umesto 4. Za reviziju je ova razlika odlučujuća: pravilo koje glasi „AES-256 prema ISO 32000-2” ispunjava isključivo R6, dok R5 datoteka koja sebe naziva AES-256 ne ispunjava to pravilo iako prolazi jednostavnu proveru jačine. Klasni sloj ih razlikuje po imenu, esAES256Bit za R5 naspram esAES256BitAcroX za R6, a svojstvo EncryptionAcroX odgovara na pitanje o reviziji pomoću jedne logičke vrednosti.

Bitovi dozvola i njihov sitan tekst o dužini ključa

EncodePermissions pakuje osam flegova u ceo broj koji očekuju funkcije Encrypt i EncryptFile. Osnovni skup čine štampanje (print), kopiranje (copy), izmena (change) i dodavanje beleški (add-notes); prošireni skup čine popunjavanje polja (fill-fields), kopiranje radi pristupačnosti (copy-for-accessibility), sastavljanje (assemble) i štampanje u punom kvalitetu (full-quality print). Sitan tekst, koji i sam primer šifrovanja u biblioteci jasno navodi, jeste da proširena četiri flega stupaju na snagu tek od jačine od 128 bita pa naviše. Fleg za štampanje u punom kvalitetu prati isto pravilo: ako ga isključite da biste iznudili štampanje u niskoj rezoluciji, 40-bitni dokument će to ignorisati, jer to smanjenje kvaliteta takođe zahteva 128-bitno ili jače šifrovanje. Ako upišete pravilo „samo štampanje u niskoj rezoluciji” u 40-bitnu datoteku, svaki pregledač će je ipak odštampati u punom kvalitetu.

Dublje pitanje je ko zapravo nameće bilo koji od ovih bitova, a odgovor je: niko u koga se možete pouzdati. Dozvole su instrukcije za usaglašene čitače, a ne kriptografska ograničenja. Ključ za dešifrovanje je identičan bez obzira na to da li je kopiranje dozvoljeno ili zabranjeno, tako da zaključan skup dozvola samo drži poštene pregledače poštenim. Čitač koji odluči da ignoriše ove bitove ne suočava se ni sa kakvom kriptografskom preprekom. Ako je obaveza da se spreči ekstrakcija, a ne samo da se obeshrabri, datoteci je potrebna korisnička lozinka, a radni proces zahteva kontrole na nivou samog procesa obrade, pa bi revizorski izveštaj trebalo da navede pod kojim od ova dva režima se svaka datoteka zapravo nalazi, umesto da fleg dozvole tretira kao bravu.

Postavljanje pravila i dokazivanje da su primenjena

Primena šifrovanja na postojeće datoteke ne zahteva njihovo učitavanje u stablo objekata. EncryptFile obrađuje ulaz u izlaz u jednom jedinom pozivu, a revizorska petlja ponovo otvara rezultat kako bi potvrdila šta je zaista upisano na disk. Isporučeni primer šifrovanja prati isti šablon upisa i naknadnog čitanja:

var
  PDF: TPDFlib;
  R: Integer;
begin
  PDF := TPDFlib.Create;
  try
    R := PDF.EncryptFile('in.pdf', 'out.pdf', 'owner-secret', 'user-secret', 4,
      PDF.EncodePermissions(1, 0, 0, 0,    // print allowed; copy/change/notes denied
                            0, 0, 0, 1));  // extended set: full-quality print only
    if (R = 1) and (PDF.LoadFromFile('out.pdf', 'user-secret') = 1) then
    begin
      Writeln('algorithm = ', PDF.EncryptionAlgorithm);
      Writeln('strength  = ', PDF.EncryptionStrength);
      Writeln('owner pw accepted: ', PDF.CheckPassword('owner-secret'));
    end;
  finally
    PDF.Free;
  end;
end;

Timovi koji rade na nivou dokumenata dobijaju istu operaciju sa tipiziranim skupovima umesto pakovanja bitova, što olakšava pregled koda bez naprezanja očiju:

if not Doc.Encrypt('owner-secret', 'user-secret', esAES256BitAcroX,
  [ppCanPrint], [ppCanPrintFull]) then
  raise Exception.Create('Encryption failed');

U svakom slučaju, korak naknadnog čitanja nije opciona ceremonija. On otkriva greške u primeni koje bi se inače pojavile mesecima kasnije na računaru korisnika: stara verzija biblioteke koja tiho degradira zahtevanu jačinu, izlazna putanja koja nikada nije upisana jer je direktorijum bio samo za čitanje, ili celobrojna vrednost dozvola čiji su argumenti uneti pogrešnim redosledom. Sva tri slučaja prolaze lokalni brzi test, a padaju na terenu, dok ponovno otvaranje izlaza pretvara svaki od njih u izuzetak koji vidite tokom samog pokretanja koje je kreiralo datoteku. GetEncryptionFingerprint vraća kompaktnu vrednost koju možete sačuvati uz zapis o poslu, tako da kasnija poređenja mogu reći da li dva izlaza dele istu konfiguraciju šifrovanja bez ponovnog otvaranja bilo kog od njih.

Lažno pozitivni rezultati revizije vredni pisanja koda

Nekoliko šablona pouzdano navodi bezbednosne skenere na pogrešan zaključak, a svaki od njih proizilazi iz pretvaranja višedelnog pitanja u jednostavan da/ne odgovor. Najčistiji primer je Identity filter šifrovanja. Rečnik /Encrypt je prisutan, datoteka se prijavljuje kao šifrovana, a ipak stringovi i tokovi prolaze kroz Identity filter nepromenjeni, pa je stvarni sadržaj u čistom tekstu. Rešenje je čitanje StringFilterIdentity and StreamFilterIdentity pre nego što bilo šta proglasite zaštićenim.

Podela metapodataka je suptilnija. Svojstvo EncryptMetadata se može razlikovati od ostatka dokumenta u oba smera, ostavljajući šifrovanu datoteku sa čitljivim XMP paketom ili, ređe, obrnuto. Izjava „datoteka je šifrovana” ne govori ništa o tome da li su njeni metapodaci šifrovani, što postaje važno onog trenutka kada indeksator ili pravilo usmeravanja posegnu za naslovom. Ugrađene datoteke dodaju treću osu: PDF dozvoljava namenski filter šifrovanja samo za priloge, tako da prilozi mogu biti jedini šifrovani deo inače otvorenog dokumenta, ili jedini deo u čistom tekstu unutar šifrovanog dokumenta. Zabeležite ova tri dodeljena filtera kao zasebna polja za stringove, tokove i ugrađene datoteke, i nijedna od ovih zamki vas ne može uhvatiti. Sačuvajte samo jednu logičku vrednost i pogrešna procena je samo pitanje vremena.

Uklanjanje šifrovanja i izbor za nove datoteke

Revizija se često završava odlukom o uklanjanju zaštite, a mehanika tu nije prepreka. DecryptFile(InputFileName, OutputFileName, Password) upisuje dešifrovanu kopiju bez potpunog učitavanja, a Decrypt na nivou učitanog dokumenta radi isto to u memoriji kada je datoteka već otvorena. Oba zahtevaju važeću lozinku; nijedan ne zaobilazi kriptografiju. Prava prepreka su pravila poslovanja (policy), a ne kod, pa neka vaša ulazna pravila jasno definišu kada je uklanjanje dozvoljeno i zabeležite klasu lozinke koja ga je odobrila, jer sam tehnički korak ne ostavlja trag.

Izbor za novi izlaz je uži nego što to sugeriše pet vrednosti za Strength. Koristite Strength 4, AES-256 reviziju 6, osim ako ne morate da otvarate datoteku u pregledačima starijim od verzije Acrobat X. Strength 2, AES-128, predstavlja pragmatični minimum za zastarelu mrežu pregledača koji se ne mogu nadograditi. Opcije RC4 pod vrednostima 0 i 1 postoje da biste mogli da čitate i vršite reviziju istorijskih arhiva, a ne da biste kreirali bilo šta novo pomoću njih; posezanje za njima u projektima iz 2026. godine znak je da su zahtevi zastareli.

Stanje šifrovanja direktno utiče na odluke o potpisivanju, s obzirom na to da radno okruženje koje validira i potpisuje dokumente zahteva istu disciplinu naknadnog čitanja na kojoj se zasniva i ova revizija. Ta tema je pokrivena u članku o radnom okruženju za usaglašenost i potpisivanje. Kada se funkcija EncryptFile primenjuje na hiljade velikih dokumenata u paketu, vodič za direktan pristup velikim PDF datotekama pokazuje kako održati potrošnju memorije niskom i stabilnom tokom rada. Kompletna referenca za API šifrovanja nalazi se na PDFlibPas stranici proizvoda.