PDF Library for Delphi, şifreli bir PDF'teki yanlış bir parolayı, az önce başarısız olan TPDFDocument'i atarak ve bir sonraki deneme için tamamen yenisini oluşturarak yeniden dener; bu, vazgeçmeden önce en fazla on altı deneme için çalışan bir OnPassword geri çağırması (TPDFlibPasswordEvent) tarafından yönlendirilir. Bu, çoğu Delphi geliştiricisinin önce başvurduğu içgüdüden kasıtlı bir sapmadır: belleğe zaten oturan belge nesnesini tutmak, ona düzeltilmiş bir parola vermek ve sıfırdan başlamak yerine yerinde tekrar yüklemek. PDF Library for Delphi'ın v3.245.0'da eklenen yeniden deneme döngüsü, başarısız bir parola denemesinin arkasında ne bıraktığına özgü nedenlerle tersi konumu alır. Bunun arkasındaki senaryo yeterince sıradandır ki çoğu belge-yoğun Delphi uygulaması sonunda buna çarpar: bir giriş ekranı bir PDF kabul eder, şifreli bir arka plan bir parola diyaloğunu zorlar, operatör dizeyi yanlış yazar ve diyalog ikinci bir deneme için yeniden görünür. Bu kullanıcı deneyimi hakkında alışılmadık hiçbir şey yoktur; bu yüzden onun arkasındaki kod, aynı dosya için birden fazla aday parolayı kabul etmek zorundadır ve bunu güvenli bir şekilde, reddedilen denemeden durumu izleyen denemeye sızdırmadan yapmak zorundadır
Neden aynı belge nesnesinde yeniden deneyemezsiniz?
Parola denemeleri boyunca bir TPDFDocument'i yeniden kullanmak işe yaramaz, çünkü başarısız bir deneme o nesneyi bir tür durakladırılmış, devam ettirilebilir durumda bırakmak yerine dahili olarak zaten yıkmıştır. Şifreli bir PDF'i açmak, çapraz referans tablosunu ayrıştırmak, altta yatan kaynak üzerinde bir okuyucu kurmak ve sağlanan her ne parola varsa ondan bir şifreleme işleyicisi kurmak anlamına gelir, hepsi PDF Library for Delphi o parolanın doğru olup olmadığını test edebilmeden önce. Parola yanlış çıktığında, belgenin dahili yükleme rutini, tam olarak yapması gerektiği gibi, başarısız olmanın bir parçası olarak okuyucuyu, çapraz referans tablosunu ve şifreleme işleyicisini temizler; bu, ikinci bir çağrıda düzeltilmiş bir parolayı bekleyen yarım kurulmuş bir ayrıştırıcının orada oturmadığı anlamına gelir. Aynı nesneyi yine de başka bir yükleme denemesinden geçirin ve başarısızlık modu tam olarak hata ayıklaması berbat türden olur: farklı, zaten başarısız olmuş bir ayrıştırma için kurulmuş dahili durumdan bir hata yüzeye çıkar; hiçbir şeyi belirgin şekilde üç çağrı akış yukarısındaki parolaya geri işaret etmez. PDF Library for Delphi, açılmayı başaramamış bir belge nesnesini asla kurtarmaya çalışmayarak sorunun tüm sınıfından kaçınır; her deneme, okuyucu ve çapraz referans tablosu dahil, hiç yanlış bir parola görmemiş bir belge alır
OnPassword geri çağırması bir sonraki parolayı nasıl ister?
TPDFlibPasswordEvent, PDF Library for Delphi'ın az önce denenen parola yanlış çıktığında TPDFlib.LoadFromFile, LoadFromStream ve LoadFromString aracılığıyla çağırdığı geri çağırma türüdür ve işleyiciye üç şey verir: hangi denemenin çalışmak üzere olduğu, bir sonraki adayla üzerine yazılacak bir Password parametresi ve varsayılan olarak false olan bir Retry bayrağı
TPDFlibPasswordEvent = procedure(Sender: TObject; AttemptNumber: Integer;
var Password: WideString; var Retry: Boolean) of object;
property OnPassword: TPDFlibPasswordEvent read FOnPassword write FOnPassword;
Orijinal LoadFromFile çağrısına geçirilen parola, birinci deneme olarak sayılır; bu yüzden OnPassword ilk kez tetiklendiğinde, AttemptNumber 2 olarak gelir. Retry'ı ayarlanmamış bırakın ve yükleme LastErrorCode 404 ile temizce başarısız olur; onu true'ya ayarlayın ve PDF Library for Delphi, işleyicinin Password'e az önce yazdığı her neyse onunla tekrar dener
Yeniden deneme döngüsünün içinde: her deneme için yeni bir TPDFDocument
Dahili olarak, PDF Library for Delphi nesne-yaşam-döngüsü sorusunu LoadFromFile, LoadFromStream ve LoadFromString için aynı şekilde yanıtlar: birinci dahil her deneme, taze bir TPDFDocument kurar, o denemenin kullandığı her ne parolaysa onunla tam açma dizisinden geçirir ve parola doğrulanırsa nesneyi yalnızca o zaman tutar. Reddedilen bir denemenin TPDFDocument'i hemen serbest bırakılır, okuyucusunu, çapraz referans tablosunu ve şifreleme işleyicisini de birlikte götürür ve bir sonraki deneme hiç geçmişi olmayan bir nesneyle yeniden başlar
// LoadFromFile içinden sadeleştirilmiş bir alıntı: her deneme,
// daha önce reddedilmiş bir parola hiç görmemiş bir belge alır. FileName,
// AttemptNumber ve AttemptPassword çevreleyen metottan gelir.
Var
Doc: TPDFDocument;
LoadResult: TPLLoadResult;
Success: Boolean;
Begin
Success := False;
Repeat
Doc := TPDFDocument.Create;
Doc.DecodeMode := FDefaultDecodeMode;
Try
LoadResult := Doc.LoadFromFile(FileName, AttemptPassword);
Success := LoadResult = lrOkay;
if Success then
begin
FDocs.Add(Doc); // doğrulanmış belgeyi çağıranın
Doc := nil; // koleksiyonuna teslim et; aşağıdaki Free'yi atla
end;
Finally
Doc.Free; // reddedilen bir denemenin okuyucusu, çapraz
End; // referans tablosu ve şifreleme işleyicisi tam burada yıkılır
if Success or (LoadResult <> lrWrongPassword) then
Break; // başarı veya parola dışı bir hata: dur
Inc(AttemptNumber);
Until not RequestPasswordRetry(AttemptNumber, AttemptPassword);
End;
Finally bloğundan hemen önceki o Doc := nil satırı, nesne-yaşam-döngüsü sözleşmesinin tamamıdır, tek bir ifadede. Başarısız olan bir belge, tasarım gereği, yarım kurulmuş ayrıştırıcı durumunu mezara kadar taşır ve başarılı olan bir belge, TPDFlib'in çağıranın açık tuttuğu her belge için sakladığı koleksiyon olan FDocs'a hiç eklenen tek belgedir. Reddedilen bir deneme hakkında hiçbir şey yeniden deneme döngüsünün dışından görünmez: ne yarım başlatılmış bir okuyucu, ne bayat bir sayfa sayısı, ne de yanlış anahtardan kurulmuş bir şifreleme işleyicisi
PDF Library for Delphi yanlış bir parolayı kaç kez yeniden dener?
PDF Library for Delphi, çağrının kendisine geçirilen parolayı birinci deneme olarak sayarak, tek bir LoadFromFile, LoadFromStream veya LoadFromString çağrısına karşı toplam on altı denemeye izin verir. OnPassword yalnızca ikinciden on altıncıya kadar denemeler için tetiklenir ki bu geri çağırmayı on beş çağrıyla sınırlar; on yedinci bir deneme isteyin ve PDF Library for Delphi işleyiciyi hiç çağırmadan reddeder. Retry'ı herhangi bir noktada varsayılanı false'ta bırakın veya doğru bir parola olmadan on altı denemenin tümünü tüketin ve LoadFromFile, LastErrorCode PDF Library for Delphi'ın reddedilen bir parola için kodu olan 404'e ayarlanmış olarak 0 döndürür. Sınır, düzenlilik ötesinde nedenlerle vardır: sınırsız bir yeniden deneme döngüsü, özellikle bir işleyici, bir diyalogda tıklayan bir insan yerine daha önce görülen parolaların bir listesi gibi otomatikleştirilmiş bir şeye bağlandığında, bir yanlış yazılmış parolayı yüklemeyi çalıştıran her ne iş parçacığıysa ona karşı kazara bir hizmet reddine dönüştürmenin kolay bir yoludur. PDF Library for Delphi ayrıca, Sender o aynı nesne olarak geldiğinden, işleyicinin içinden TPDFlib örneği üzerinde Abort'un çağrılmasına saygı gösterir; bir parola diyaloğunda bir İptal düğmesinin arkasında kullanışlıdır ve Retry ne olarak ayarlanmış olursa olsun bir sonraki kontrolde yeniden deneme döngüsünü durdurur. Yanlış bir parola dışında bir nedenle başarısız olan bir yükleme -örneğin hasarlı bir çapraz referans tablosu- hiçbir zaman yeniden deneme döngüsüne girmez: PDF Library for Delphi LastErrorCode 401'i bildirir ve ilk denemeden sonra durur, çünkü hiçbir sayıda parola tahmini yapısal olarak bozuk bir dosyayı düzeltmez
Yeniden deneme döngüsü dosyalar, akışlar ve dizeler için aynı şekilde mi çalışır?
OnPassword geri çağırması ve on altı-deneme sınırı, LoadFromFile, LoadFromStream ve LoadFromString genelinde özdeş davranır, ama üç giriş noktası denemeler arasında kaynaklarını farklı şekilde tutar. Her deneme yalnızca adlandırılmış dosyayı yeniden açtığından, bir dosya yolunu yeniden ziyaret etmek ucuzdur ve bir dize kaynağı zaten çağıranın kendi kopyası olarak bellekte oturur; bu yüzden hiçbiri denemeler arasında çağırandan yardıma ihtiyaç duymaz. Çağıran tarafından sağlanan bir akış, üzerinde durulmaya değer tek durumdur: LoadFromStream, ilk ayrıştırma denemesinden önce o akışı konum sıfıra geri sarar ve dahili olarak kopyalar; bu yüzden sonraki her deneme, ve arkasındaki taze kurulmuş TPDFDocument, başarısız bir ayrıştırmanın akışın konumunu nerede bıraktığından değil, o dahili kopyadan yeniden oynatır. Şifre korumalı bir belge için PDF Library for Delphi'a bir TFileStream veya TMemoryStream verin ve yeniden denemeler arasında onu geri sarmaya gerek yoktur; PDF Library for Delphi, birinci, başarısız bir denemenin taşımış olabileceği bir konumu zaten hesaba katar
Parola yeniden denemesini bir belge giriş ekranına oturtmak
Bir belge giriş iş akışı, bu geri çağırma için doğal bir ev sağlar, çünkü tam olarak OnPassword'ün çözmek için kurulduğu sorun şeklidir: bir dosya uygulamanın dışından gelir, parolası önceden kesinlikle bilinmez ve aday sağlayan kişi, çevredeki kodun LoadFromFile'ın etrafına kendi yeniden deneme döngüsünü yazmasına gerek kalmadan birden fazla tahmine ihtiyaç duyar
procedure TIntakeForm.SupplyPassword(Sender: TObject; AttemptNumber: Integer;
var Password: WideString; var Retry: Boolean);
var
Typed: string;
begin
// AttemptNumber 2'den başlar: önceden denenen parola 1. denemeydi.
Typed := '';
Retry := InputQuery('Password required',
Format('Attempt %d of 16 - enter the document password', [AttemptNumber]), Typed);
if Retry then
Password := Typed;
// Operatör iptal ettiğinde Retry False olur, bu da
// LastErrorCode'u çağıranın bildirmesi için 404'te bırakır.
end;
procedure TIntakeForm.LoadInboundDocument;
var
Lib: TPDFlib;
begin
Lib := TPDFlib.Create;
try
Lib.OnPassword := SupplyPassword;
if Lib.LoadFromFile('inbound-invoice.pdf', '') = 1 then
RegisterIntakeDocument(Lib) // buraya yalnızca doğrulanmış bir belge ulaşır
else
LogRejectedIntake('inbound-invoice.pdf', Lib.LastErrorCode);
finally
Lib.Free;
end;
end;
RegisterIntakeDocument, Lib'i yalnızca LoadFromFile 1 döndürdükten sonra alır, bu o alışverişteki bir parolanın gerçekten dosyanın şifreleme işleyicisine karşı doğrulandığı anlamına gelir; reddedilen bir deneme o satıra hiç ulaşmaz, ne de yarı açık bir belge ulaşır. Böyle bir belge açık olarak onaylandığında, sırada gelen şey, işe yarayan parolanın tüm güvenlik hikayesi olduğunu varsaymak yerine koruma ayarlarına ikinci bir bakışa değer: bir belgenin /Encrypt sözlüğünün gerçekte neyi bildirdiğini denetleme makalesi, böyle bir dosya yüklendiğinde PDF Library for Delphi'ın sunduğu algoritma, revizyon ve izin bitlerini okumayı ele alır
Parola yeniden deneme, aynı zamanda PDF Library for Delphi'ın ayrıştırma katmanı boyunca uyguladığı daha geniş bir disiplinin dar bir örneğidir: kendini henüz kanıtlamamış bir dosya, sorunun hangi parolanın kilidini açtığı mı yoksa içindeki bir uzunluk alanının ihtiyaç duyduğu arabellek boyutu hakkında yalan söyleyip söylemediği mi olduğuna bakılmaksızın, şüphenin faydasını almaz. Bir Pascal PDF ayrıştırıcısını kötü niyetli dosyalara karşı sertleştirmek, o disiplinin diğer yarısını ele alır: gelen bir PDF'teki her yazı tipi programını ve görüntü akışını, yalnızca parolasını unutmuş iyi biçimlendirilmiş bir belge değil, düşman girdisi olarak ele alan kod çözücüler
OnPassword ve arkasındaki yeniden deneme döngüsü, LoadFromFile, LoadFromStream veya LoadFromString'in zaten bulunduğu her yerde mevcut olan, Delphi ve C++Builder için standart PDF Library for Delphi PDF kütüphanesi'nin bir parçasıdır; parolasında yalnızca ikinci bir tahmine ihtiyaç duyan bir belge için ayrı bir modül veya lisans katmanı gerekmez