PDF Library for Delphi yrittää väärää salasanaa uudelleen salatussa PDF:ssä hylkäämällä TPDFDocument-olion, joka juuri epäonnistui, ja luomalla täysin uuden seuraavaa yritystä varten, ohjattuna OnPassword-takaisinkutsulla (TPDFlibPasswordEvent), joka ajetaan jopa kuudentoista yrityksen ajan ennen luovuttamista. Se on tarkoituksellinen poikkeama vaistosta, johon useimmat Delphi-kehittäjät turvautuvat ensin: pidä muistissa jo istuva asiakirjaolio, syötä sille korjattu salasana, ja lataa uudelleen paikallaan alusta aloittamisen sijaan. PDF Library for Delphi:n uudelleenyrityssilmukka, lisätty v3.245.0:ssa, ottaa vastakkaisen kannan, syistä, jotka ovat spesifisiä sille, mitä epäonnistunut salasanayritys jättää jälkeensä. Sen takana oleva skenaario on riittävän tavallinen, että useimmat asiakirjapainotteiset Delphi-sovellukset osuvat siihen lopulta: vastaanottonäyttö hyväksyy PDF:n, salattu jälki pakottaa salasanadialogin, operaattori näppäilee merkkijonon väärin, ja dialogi ilmestyy uudelleen toista yritystä varten. Mikään tuossa käyttäjäkokemuksessa ei ole epätavallista, joten sen takana olevan koodin on hyväksyttävä useampi kuin yksi ehdokassalasana samalle tiedostolle, ja sen on tehtävä se turvallisesti, vuotamatta tilaa hylätystä yrityksestä seuraavaan
Mikset voi vain yrittää uudelleen samalla asiakirjaoliolla?
TPDFDocument-olion uudelleenkäyttö salasanayritysten yli ei toimi, koska epäonnistunut yritys on jo purkanut tuon olion sisäisesti sen sijaan, että jättäisi sen johonkin keskeytettyyn, jatkettavissa olevaan tilaan. Salatun PDF:n avaaminen tarkoittaa ristiviittaustaulun jäsentämistä, lukijan rakentamista taustalla olevan lähteen yli, ja salauskäsittelijän rakentamista mistä tahansa toimitetusta salasanasta, kaikki ennen kuin PDF Library for Delphi voi edes testata, onko tuo salasana oikea. Kun salasana osoittautuu vääräksi, asiakirjan sisäinen latausrutiini siivoaa lukijan, ristiviittaustaulun ja salauskäsittelijän osana epäonnistumista, täsmälleen kuten sen pitäisi, mikä tarkoittaa, ettei siellä ole mitään puoliksi rakennettua jäsennintä odottamassa korjattua salasanaa toisella kutsulla. Aja sama olio silti läpi toisen latausyrityksen, ja vikatila on juuri sellainen, joka on kurja debugata: virhe paljastuu sisäisestä tilasta, joka on rakennettu eri, jo epäonnistuneelle jäsennykselle, ilman mitään ilmeisesti osoittamassa takaisin salasanaan kolme kutsua ylävirtaan. PDF Library for Delphi välttää koko ongelmaluokan yrittämättä koskaan palauttaa asiakirjaoliota, kun se on kerran epäonnistunut avautumaan; jokainen yritys saa asiakirjan, joka ei ole koskaan nähnyt väärää salasanaa, lukija ja ristiviittaustaulu mukaan lukien
Miten OnPassword-takaisinkutsu pyytää seuraavaa salasanaa?
TPDFlibPasswordEvent on takaisinkutsutyyppi, jonka PDF Library for Delphi kutsuu TPDFlib.LoadFromFile-, LoadFromStream- ja LoadFromString-metodien kautta aina, kun juuri kokeiltu salasana osoittautuu vääräksi, ja se antaa käsittelijälle kolme asiaa: mikä yritys on menossa käynnistymään, Password-parametrin ylikirjoitettavaksi seuraavalla ehdokkaalla, ja Retry-lipun, joka oletusarvoistuu falseksi
TPDFlibPasswordEvent = procedure(Sender: TObject; AttemptNumber: Integer;
var Password: WideString; var Retry: Boolean) of object;
property OnPassword: TPDFlibPasswordEvent read FOnPassword write FOnPassword;
Alkuperäiseen LoadFromFile-kutsuun välitetty salasana lasketaan yritykseksi yksi, joten ensimmäisellä kerralla, kun OnPassword laukeaa lainkaan, AttemptNumber saapuu arvolla 2. Jätä Retry asettamatta, ja lataus epäonnistuu puhtaasti LastErrorCode-arvolla 404; aseta se todeksi, ja PDF Library for Delphi yrittää uudelleen millä tahansa, mitä käsittelijä juuri kirjoitti Password-kenttään
Uudelleenyrityssilmukan sisällä: uusi TPDFDocument jokaiselle yritykselle
Sisäisesti PDF Library for Delphi vastaa oliokiertokysymykseen samalla tavalla LoadFromFile-, LoadFromStream- ja LoadFromString-metodeille: jokainen yritys, ensimmäinen mukaan lukien, rakentaa tuoreen TPDFDocument-olion, ajaa sen läpi täyden avaussekvenssin millä tahansa salasanalla, jota tuo yritys käyttää, ja pitää olion vain, jos salasana todentuu. Hylätyn yrityksen TPDFDocument vapautetaan välittömästi, viedessään mukanaan lukijansa, ristiviittaustaulunsa ja salauskäsittelijänsä, ja seuraava yritys alkaa alusta oliolla, jolla ei ole lainkaan historiaa
// Yksinkertaistettu ote LoadFromFile-metodin sisältä: jokainen yritys saa
// asiakirjan, joka ei ole koskaan nähnyt aiemmin hylättyä salasanaa. FileName,
// AttemptNumber ja AttemptPassword tulevat ympäröivästä metodista.
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); // luovuta vahvistettu asiakirja
Doc := nil; // kutsujan kokoelmaan; ohita alla oleva Free
end;
Finally
Doc.Free; // hylätyn yrityksen lukija, xref-taulukko
End; // ja salausobjekti puretaan juuri tässä
if Success or (LoadResult <> lrWrongPassword) then
Break; // onnistuminen tai muu kuin salasanavirhe: pysäytä
Inc(AttemptNumber);
Until not RequestPasswordRetry(AttemptNumber, AttemptPassword);
End;
Tuo Doc := nil -rivi juuri ennen Finally-lohkoa on koko oliokiertosopimus yhdessä lauseessa. Asiakirja, joka epäonnistuu, kantaa puoliksi rakennetun jäsennintilansa hautaan mukanaan, suunnitellusti, ja asiakirja, joka onnistuu, on ainoa koskaan lisätty FDocs:iin, kokoelmaan, jota TPDFlib pitää jokaiselle asiakirjalle, jonka kutsuja pitää auki. Mikään hylätystä yrityksestä ei ole näkyvissä uudelleenyrityssilmukan ulkopuolelta: ei puoliksi alustettu lukija, ei vanhentunut sivumäärä, ei väärästä avaimesta rakennettu salauskäsittelijä
Kuinka monta kertaa PDF Library for Delphi yrittää väärää salasanaa uudelleen?
PDF Library for Delphi sallii yhteensä kuusitoista yritystä yhtä LoadFromFile-, LoadFromStream- tai LoadFromString-kutsua vastaan, laskien itse kutsuun välitetyn salasanan yritykseksi yksi. OnPassword laukeaa vain koskaan yrityksille kaksi kuuteentoista, mikä rajaa takaisinkutsun viiteentoista kutsuun; pyydä seitsemättätoista yritystä, ja PDF Library for Delphi kieltäytyy jopa kutsumatta käsittelijää. Jätä Retry oletusarvoonsa false missä tahansa kohdassa, tai kuluta kaikki kuusitoista yritystä ilman oikeaa salasanaa, ja LoadFromFile palauttaa 0:n LastErrorCode-arvolla 404, PDF Library for Delphi:n koodilla hylätylle salasanalle. Katto on olemassa muistakin syistä kuin siisteydestä: rajoittamaton uudelleenyrityssilmukka on helppo tapa muuttaa yksi väärin kirjoitettu salasana vahingossa tapahtuvaksi palvelunestohyökkäykseksi mitä tahansa säiettä vastaan, joka ajaa latausta, varsinkin heti kun käsittelijä on kytketty johonkin automatisoituun, kuten listaan aiemmin nähtyjä salasanoja, ihmisen klikkaillessa läpi dialogia sen sijaan. PDF Library for Delphi kunnioittaa myös Abort-kutsua, joka tehdään TPDFlib-instanssille käsittelijän sisältä, koska Sender saapuu samana oliona, hyödyllinen Cancel-painikkeen takana salasanadialogissa, ja pysäyttää uudelleenyrityssilmukan seuraavassa tarkistuksessa riippumatta siitä, mihin Retry oli asetettu. Lataus, joka epäonnistuu muusta syystä kuin väärästä salasanasta, vioittuneesta ristiviittaustaulusta esimerkiksi, ei koskaan astu uudelleenyrityssilmukkaan lainkaan: PDF Library for Delphi raportoi LastErrorCode 401:n ja pysähtyy ensimmäisen yrityksen jälkeen, koska mikään määrä salasana-arvauksia ei korjaa rakenteellisesti rikkinäistä tiedostoa
Toimiiko uudelleenyrityssilmukka samalla tavalla tiedostoille, virroille ja merkkijonoille?
OnPassword-takaisinkutsu ja kuudentoista yrityksen katto käyttäytyvät identtisesti LoadFromFile-, LoadFromStream- ja LoadFromString-metodeissa, vaikka nämä kolme sisääntulopistettä pitävät lähteestään kiinni eri tavoin yritysten välillä. Tiedostopolku on halpa vierailla uudelleen, koska jokainen yritys yksinkertaisesti avaa nimetyn tiedoston uudelleen, ja merkkijonolähde jo istuu muistissa kutsujan omana kopiona, joten kumpikaan ei tarvitse mitään apua kutsujalta yritysten välillä. Kutsujan toimittama virta on se yksi tapaus, joka kannattaa pysähtyä miettimään: LoadFromStream hakeutuu tuon virran takaisin nollapositioon ja kopioi sen sisäisesti ennen ensimmäistä jäsennysyritystä, joten jokainen sitä seuraava yritys, ja sen takana oleva juuri rakennettu TPDFDocument, toistaa tuosta sisäisestä kopiosta eikä siitä, mihin epäonnistunut jäsennys jätti virran position. Anna PDF Library for Delphi:lle TFileStream tai TMemoryStream salasanasuojatulle asiakirjalle, eikä sitä tarvitse kelata takaisin uudelleenyritysten välillä; PDF Library for Delphi ottaa jo huomioon position, jonka ensimmäinen, epäonnistunut yritys on saattanut siirtää
Salasanan uudelleenyrityksen sovittaminen asiakirjan vastaanottonäyttöön
Asiakirjan vastaanottotyönkulku on luonnollinen koti tälle takaisinkutsulle, koska se on juuri sellainen ongelmamuoto, jota varten OnPassword rakennettiin: tiedosto saapuu sovelluksen ulkopuolelta, sen salasanaa ei tiedetä varmuudella etukäteen, ja ehdokkaita toimittavan henkilön on saatava useampi kuin yksi arvaus ilman että ympäröivä koodi kirjoittaa oman uudelleenyrityssilmukkansa LoadFromFile:n ympärille
procedure TIntakeForm.SupplyPassword(Sender: TObject; AttemptNumber: Integer;
var Password: WideString; var Retry: Boolean);
var
Typed: string;
begin
// AttemptNumber laskee alkaen luvusta 2: jo kokeiltu salasana oli yritys 1.
Typed := '';
Retry := InputQuery('Password required',
Format('Attempt %d of 16 - enter the document password', [AttemptNumber]), Typed);
if Retry then
Password := Typed;
// Retry on False, kun käyttäjä peruuttaa, jolloin
// LastErrorCode jää arvoon 404 kutsujan raportoitavaksi.
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) // vain vahvistettu asiakirja pääsee tänne asti
else
LogRejectedIntake('inbound-invoice.pdf', Lib.LastErrorCode);
finally
Lib.Free;
end;
end;
RegisterIntakeDocument vastaanottaa Lib:n vain koskaan sen jälkeen, kun LoadFromFile on palauttanut 1:n, mikä tarkoittaa, että jokin salasana tuossa vaihdossa todella todentui tiedoston salauskäsittelijää vasten; hylätty yritys ei koskaan saavuta tuota riviä, eikä myöskään puoliksi avoin asiakirja. Se, mitä seuraavaksi tulee, heti kun tällainen asiakirja on vahvistettu auki, kannattaa vielä toinen katsaus sen suojausasetuksiin sen sijaan, että oletettaisiin salasanan, joka toimi, olevan koko tietoturvatarina: asiakirjan /Encrypt-sanakirjan todellisen ilmoituksen tarkastaminen käsittelee algoritmin, revision ja käyttöoikeusbittien lukemisen, jotka PDF Library for Delphi paljastaa heti, kun tällainen tiedosto latautuu
Salasanan uudelleenyritys on myös kapea esimerkki laajemmasta kurista, jota PDF Library for Delphi soveltaa koko jäsennyskerroksessaan: tiedosto, joka ei ole vielä todistanut itseään, ei saa epäilyksen etua, olipa kysymys siitä, mikä salasana avaa sen, tai siitä, valehteleeko sen sisällä oleva pituuskenttä puskurin koosta, jota se tarvitsee. Pascal-PDF-jäsentimen kovettaminen haitallisia tiedostoja vastaan käsittelee tuon kurin toisen puoliskon, dekooderit, jotka kohtelevat jokaista fonttiohjelmaa ja kuvavirtaa saapuvassa PDF:ssä vihamielisenä syötteenä eikä hyvin muodostettuna asiakirjana, joka vain unohti salasanansa
OnPassword ja sen takana oleva uudelleenyrityssilmukka ovat osa vakiomuotoista PDF Library for Delphi PDF-kirjastoa Delphille ja C++Builderille, saatavilla missä tahansa, missä LoadFromFile, LoadFromStream tai LoadFromString jo ovat, ilman erillistä moduulia tai lisenssitasoa vaadittuna asiakirjalle, joka vain tarvitsee toisen arvauksen salasanaansa