Zastavica dozvole (permission flag) nije bezbednosni mehanizam. Bit koji kaže "zabranjeno kopiranje" živi unutar istog rečnika /Encrypt kao i sama kriptografija, što mu daje privid sprovođenja pravila koji zapravo nema, i onog trenutka kada ovo dvoje počnete da posmatrate kao jedno, vaša revizija (audit) počinje da daje pogrešne odgovore. Jedino pitanje koje vredi postaviti u vezi sa jednim PDF-om nije "da li je šifrovan". Ono je specifičnije i teže: koji algoritam, koja revizija rukovaoca bezbednošću (security handler revision), koja od dve lozinke je postavljena, koji bitovi dozvola su zahtevani i koje delove datoteke enkripcija zapravo dotiče. Datoteka može biti formalno šifrovana, a praktično otvorena. Može odbiti da bude pročitana, a ipak ostaviti svoje metapodatke u čistom tekstu (plaintext). Može zaključati štampanje u zastavici koju je bilo koji pregledač slobodan da ignoriše. Revizija PDF-a znači odvojeno rešavanje svakog od ovih pitanja, a PDFlibPas, losLab-ov PDF mehanizam za Delphi i C++Builder, izlaže svako od njih i kroz ravni API sa celobrojnim hendlerima i kroz tipizirani sloj klasa
Šta rečnik /Encrypt zapravo beleži
Standard ISO 32000-1 §7.6 definiše bezbednost dokumenata kroz nekoliko unosa u rečniku, a PDFlibPas ih preslikava jedan na jedan u zapisu TPDFEncryption. 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 (sa dodatim OE i UE za AES-256), zastavica EncryptMetadata ide uporedo, a još tri polja imenuju kriptografske filtere (crypt filters) primenjene na stringove, tokove (streams) i ugrađene datoteke respektivno
Vrednost ovog zapisa je u tome što on ništa ne tumači umesto vas. On vam vraća sirovi rečnik i prepušta vam da donesete zaključke, što je upravo ono što je potrebno jednoj reviziji. Slučaj "čisti tekst unutar šifrovanog" (plaintext-inside-encrypted) se pojavljuje u StringFilterIdentity i StreamFilterIdentity: kada je bilo koji od njih tačan, odgovarajući podaci prolaze kroz Identity filter netaknuti, bez obzira na to šta izveštava šifrovani status dokumenta. Skener koji se zaustavi na konstataciji "prisutan je /Encrypt rečnik" nazvaće takvu datoteku zaštićenom iako njeni stringovi i tokovi leže u čitljivom obliku. Ista nijansa upravlja metapodacima. Kada je EncryptMetadata netačno, XMP paket ostaje čitljiv svakom indeksatoru, dok sadržaj stranice nije, što je vredno znati onog trenutka kada se vaša pravila usmeravanja (routing rules) oslanjaju na polje naslova ili autora
Kratka bezbednosna provera pomoću ravnog API-ja
Za većinu procesnih linija (pipelines), četiri ravna poziva daju odgovore na svakodnevna pitanja. Poziv LoadFromFile vraća 1 u slučaju uspeha, a kada je dokument otvoren, inspektori enkripcije daju izveštaj u odnosu na njegovo dešifrovano stanje:
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;
Poziv CheckPassword je važniji nego što to njegova jednoslojna signatura sugeriše. PDF definiše dve lozinke sa nejednakom snagom. Korisnička (user) lozinka je neophodna da bi se datoteka uopšte otvorila. Vlasnička (owner) 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 radi stvari koje sesija pod korisničkom lozinkom ne može, pa revizija koja ne beleži koji je akreditiv predstavljen beleži samo pola istine. Sloj klasa čini ovu razliku upitnom. Metode TPDFDocument.HasUserPassword and 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 snage, gde "AES-256" označava dve stvari
Ravne funkcije Encrypt i EncryptFile uzimaju celobrojnu vrednost Strength sa pet značajnih 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 uveden sa verzijom Acrobat 9, i 4 za 256-bitni AES koji zahteva Acrobat X i noviji
Zanimljiv deo je to što su i 3 i 4 označeni kao AES-256, a ne predstavljaju istu šemu. Vrednost 3 se mapira u reviziju 5 bezbednosnog rukovaoca (security handler), što je prelazni dizajn koji je isporučio Acrobat 9 i koji ISO nikada nije usvojio. Vrednost 4 se mapira u reviziju 6, čija je funkcija izvođenja ključa (key-derivation function) ojačana i standardizovana u ISO 32000-2. Za dokument koji kreirate danas ne postoji razlog da izaberete 3 umesto 4. Za reviziju je ovaj jaz odlučujući: politika koja glasi "AES-256 prema standardu ISO 32000-2" zadovoljena je isključivo revizijom R6, a R5 datoteka koja sebe naziva AES-256 pada na toj politici dok prolazi kroz jednostavnu proveru jačine. Sloj klasa ih drži razdvojenim po nazivu, esAES256Bit za R5 naspram esAES256BitAcroX za R6, a svojstvo EncryptionAcroX odgovara na pitanje o reviziji pomoću jedne logičke vrednosti (boolean)
Bitovi dozvola i sitna slova o dužini ključa
Poziv EncodePermissions pakuje osam zastavica u ceo broj koji funkcije Encrypt i EncryptFile očekuju. Štampanje, kopiranje, izmena i dodavanje beleški čine osnovni skup. Popunjavanje polja, kopiranje radi pristupačnosti, sastavljanje (assemble) i štampanje u punom kvalitetu čine prošireni skup. Sitna slova, koja i sam demo primer enkripcije u biblioteci jasno navodi, jesu to da proširene četiri opcije stupaju na snagu tek na jačini od 128 bita i više. Zastavica za štampanje u punom kvalitetu potpada pod isto pravilo: obrišite je da biste forsirali štampanje niske rezolucije i 40-bitni dokument će vas ignorisati, jer to smanjenje kvaliteta takođe zahteva 128-bitnu ili jaču enkripciju. Kodirajte politiku "samo štampa niske rezolucije" u 40-bitnu datoteku i svaki pregledač će je ipak odštampati u punom kvalitetu
Dublje pitanje je ko zapravo primenjuje bilo koji od ovih bitova, a odgovor je — niko kome možete verovati. 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čani skup dozvola samo održava poštenje kod poštenih pregledača. Čitač koji odluči da ignoriše ove bitove ne suočava se ni sa kakvom kriptografskom preprekom. Ako je obaveza sprečavanje ekstrakcije, a ne samo obeshrabrivanje iste, datoteci je potrebna korisnička lozinka, a radnom toku (workflow) su potrebne kontrole na nivou procesa oko nje, i revizorski izveštaj bi trebalo da navede pod kojim od ova dva režima se svaka datoteka zapravo nalazi, umesto da tretira zastavicu dozvole kao bravu
Postavljanje politike i dokazivanje da je primenjena
Primena enkripcije na postojeće datoteke ne zahteva njihovo učitavanje u stablo objekata. Metoda EncryptFile obrađuje ulaz u izlaz u jednom pozivu, a revizorska petlja ponovo otvara rezultat da bi potvrdila šta je završilo na disku. Isporučeni demo primer enkripcije prati isti oblik "upisivanje pa ponovno čitanje":
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 prolazi reviziju koda sa daleko manje napora:
if not Doc.Encrypt('owner-secret', 'user-secret', esAES256BitAcroX,
[ppCanPrint], [ppCanPrintFull]) then
raise Exception.Create('Encryption failed');
U svakom slučaju, korak ponovnog čitanja nije opciona ceremonija. On detektuje greške u implementaciji koje bi se inače pojavile mesecima kasnije na mašini klijenta: stara verzija biblioteke koja tiho degradira zahtevanu jačinu, izlazna putanja koja nikada nije upisana jer je direktorijum bio samo za čitanje, ili ceo broj dozvola čiji su argumenti otišli pogrešnim redosledom. Sva tri slučaja prolaze lokalni test ("smoke test"), a padaju u realnom radu, dok ponovno otvaranje izlaza pretvara svaki od njih u izuzetak koji vidite tokom pokretanja koje je kreiralo datoteku. Metoda GetEncryptionFingerprint vraća kompaktan iznos koji možete sačuvati uz zapis o poslu, tako da kasnija poređenja mogu reći da li dva izlaza dele istu konfiguraciju enkripcije bez ponovnog otvaranja bilo kojeg od njih
Lažno pozitivni rezultati revizije za koje vredi pisati kod
Nekoliko šablona pouzdano navodi bezbednosne skenere na pogrešan zaključak, a svaki od njih proizilazi iz sažimanja višedelnog pitanja u odgovor tipa da-ili-ne. Identity crypt filter je najčistiji primer. 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 svojstava StringFilterIdentity i StreamFilterIdentity pre nego što bilo šta proglasite zaštićenim
Podela metapodataka je suptilnija. Zastavica EncryptMetadata se može razlikovati od ostatka dokumenta u oba smera, ostavljajući šifrovanu datoteku sa čitljivim XMP paketom ili, ređe, obrnuto. Tvrdnja "datoteka je šifrovana" ne govori ništa o tome da li su šifrovani i njeni metapodaci, što postaje važno onog trenutka kada indeksator ili pravilo usmeravanja posegne 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 odvojena polja za stringove, tokove i ugrađene datoteke, i nijedna od ovih zamki vas ne može uhvatiti. Sačuvajte jednu logičku vrednost (boolean) i pogrešan poziv je samo pitanje vremena
Uklanjanje enkripcije i njen izbor za nove datoteke
Revizija se često završava odlukom o uklanjanju zaštite, a mehanika tu ne predstavlja prepreku. Metoda DecryptFile(InputFileName, OutputFileName, Password) upisuje dešifrovanu kopiju bez potpunog učitavanja, a poziv Decrypt na učitanom dokumentu radi isto to u memoriji kada je datoteka već otvorena. Oba zahtevaju važeću lozinku. Nijedan ne zaobilazi kriptografiju. Prava prepreka je bezbednosna politika, a ne kod, pa učinite da vaša pravila prijema jasno definišu kada je uklanjanje dozvoljeno i zabeležite klasu lozinke koja je to odobrila, jer sam tehnički korak ne ostavlja nikakav trag
Izbor za novi izlaz je uži nego što sugeriše pet vrednosti jačine (Strength). Koristite jačinu 4, AES-256 revizija 6, osim ako ne morate da otvarate datoteku u pregledačima starijim od verzije Acrobat X. Jačina 2, AES-128, predstavlja pragmatičan minimum za stariju flotu pregledača koja se ne može nadograditi. RC4 opcije 0 i 1 su tu da biste mogli da čitate i vršite reviziju istorijskih arhiva, a ne da biste sa njima proizvodili bilo šta novo. Posezanje za njima u projektu iz 2026. godini znak je da je zahtev iz prethodnog procesa zastareo
Stanje enkripcije se direktno prenosi na odluke o potpisivanju, pošto radni sto koji validira i potpisuje dokumente zahteva istu disciplinu ponovnog čitanja na koju se oslanja i ova revizija. Ta tema je pokrivena u članku o radnom stolu za usklađenost i potpisivanje. Kada se grupa operacija primenjuje preko EncryptFile na hiljadama velikih dokumenata, vodič za direktan pristup velikim PDF-ovima pokazuje kako zadržati potrošnju memorije ravnom tokom rada. Kompletna referenca za API enkripcije nalazi se na stranici proizvoda PDFlibPas