v3.126.2 öncesi PDFium Component derlemelerinde TPdf.AnalyzeSignatureRevisions, gerçek bir sayfa içerik düzenlemesini DocMDP P=3 altında izin verilen bir annotation değişikliği diye notlandırabiliyordu; çünkü revizyon rol grafiği, imza widget'ının sayfasına yaptığı /P geri referansını sahiplik sayıyordu. v3.126.2'den beri PDFium Component, navigation kenarlarını owned-payload kenarlarından ayırır; böylece sayfa içeriği sayfa içeriği olarak kalır. Bu düzeltmenin ardındaki hata bildirimi kâğıt üzerinde zararsız görünür. Sertifikalı bir sözleşme annotation'lara izin verir; karşı taraf bir incremental save ekler ve çözümleyici, sonraki her değişikliğin izinli olduğunu söyler. Sonra biri render edilmiş sayfaları diff'ler ve 2. sayfadaki ödeme tutarı farklıdır
Bu yazı, imza sonrası revizyon değişiklik analizi genel bakışının saldırgan gözüyle devamıdır; revizyon yeniden kurmanın ve DocMDP notlandırmanın temellerini atlayıp doğrudan object grafiğine gider: sahiplik nasıl modellendi, bir kenarın yönü güvenlik hükmüne neden karar verir, v3.126.2'de ne değişti ve kendi kabul mantığınızı nasıl denetlersiniz
Bir sayfa düzenlemesi DocMDP P=3 altında annotation değişikliği olarak neden geçti?
Sayfa düzenlemesi geçti, çünkü eski rol grafiği sözlükteki her dolaylı referansı, referans gösterilen object referans gösterenin malıymış gibi izliyordu; imza widget'ı da sayfasına geri işaret eder. Bir annotation sözlüğü, üzerinde durduğu page object'ine dolaylı bir referans olan /P taşır (ISO 32000-1 §12.5.2). O girdi bir navigation ipucudur. Widget sayfaya sahip değildir; sayfa, widget'a kendi /Annots dizisi üzerinden sahiptir
Çözümleyici, sonraki değişiklikleri notlandırmadan önce her object'e bir rol bit kümesi atar: page, annotation, form ve validation malzemesi. Kök object'ler rollerini kendi sözlüklerinden alır ve rol sonra referans gösterdikleri her şeye yayılır. Eski yayılımda zincir şöyle gitti:
- İmza widget'ı,
/FT /Sig'li bir/Subtype /Widget'tır; dolayısıyla annotation rolünü alır - Widget'ın
/P'si, annotation rolünü çoktan page rolü taşıyan sayfa sözlüğüne iter - Sayfa iki rolü de
/Contents'e ve/Resources'e, ayrıca/Parentüzerinden Pages ağacına ve her kardeş sayfaya iter << /Length 812 >>gibi bir content stream sözlüğü/Typetaşımaz; dolayısıyla sınıflandırıcı rol bitlerine düşer ve page rolünden önce annotation rolünü kontrol ediyordu
Değiştirilmiş content stream bu yüzden prckAnnotation olarak çıkıyordu. ISO 32000-1 §12.8.2.2 uyarınca DocMDP P=3, annotation değişikliklerine izin verir; dolayısıyla karar prdAllowed oldu ve rapor prasAllowed'a toparlandı. Aynı dosya P=2 altında reddedildi ama yalnızca tesadüfen: P=2 annotation değişikliklerini yasaklar; dolayısıyla yanlış etiketli stream yanlış nedenden dolayı reddediliyordu. Sabit dört geçişli bir yayılım döngüsü ikinci bir zaaf ekledi. Dolaylı bir dizi üzerinden ya da object numaraları geriye doğru giden uzun bir zincir üzerinden ulaşan payload hiçbir rol almayabiliyordu
Bir imza doğrulayıcı neden bir object'in sahibini sormalı?
Bir imza doğrulayıcı bir object'in sahibini sormalı, çünkü PDF incremental update'leri (ISO 32000-1 §7.5.6), var olan bir object numarasını yeniden tanımlayan bir revizyon eklemeye herkese izin verir ve yeniden tanımlanan gövde ne olduğunu ilan etmez. İmza yine de doğrulanır; çünkü yalnızca kendi revizyonunun baytlarını kapsar. İmza sonrası müdahaleye karşı her savunma bu yüzden, değişen her object'i onu kullanan yapıya eşlemeye ve sonra imzalayanın o yapının değişmesine izin verip vermediğini sormaya dayanır
Yayımlanmış birkaç saldırı sınıfı tam o boşlukta çalışır. Incremental saving saldırıları, sayfa içeriğini değiştiren bir revizyon ekler ve doğrulayıcının yalnızca imzalı bayt aralığını kontrol etmesine bel bağlar. Shadow saldırıları, imzadan önce gizli içerik eker ve sonra küçük, masum görünümlü bir değişiklikle onu etkinleştirir. Sertifikalı belgelere yönelik saldırılar, P=2 ile P=3'ün bazı sonraki düzenlemelere açıkça izin verdiği gerçeğini suiistimal eder ve yasak bir düzenlemeyi izinli bir kılığına sokar. Object'leri /Type /Annot gibi etiketlerle ya da tesadüfen onlara ulaşan herhangi bir referans yoluyla sınıflandıran bir doğrulayıcı üçüncü sınıfa açıktır: saldırganın yasak olana ulaşabilen tek bir izinli yapıyı bulması yeter
Bu yüzden soru hangi object'lerin değiştiği değil, kimin onlara sahip olduğudur. Bir sayfadan /Contents üzerinden ulaşılan bir content stream, başka ne işaret ederse etsin sayfa içeriğidir. /P üzerinden sayfaya geri işaret eden bir annotation, annotation'ın nerede yaşadığını söyler, neye sahip olduğunu değil
PDFium Component v3.126.2 sahipliği nasıl modeller?
PDFium Component v3.126.2, geri referanslarını navigation sayar, onları rol yayılımının dışında tutar ve hangi anahtarların navigation sayılacağını, anahtar adından değil onları tutan sözlüğün yapısal rolünden belirler. Tablo, artık sahiplik taşımayan navigation anahtarlarını özetliyor
| Sahip sözlük | Navigation sayılan anahtarlar | Şartname referansı |
|---|---|---|
| Page ya da Pages node'u | /Parent, /Kids, /Annots | ISO 32000-1 §7.7.3 |
| Annotation ya da widget | /P | ISO 32000-1 §12.5.2 |
| Widget ya da field sözlüğü | /Parent | ISO 32000-1 §12.7.3 |
Anahtar adına göre küresel filtrelemek yeni bir delik açardı. Bir font ya da XObject kaynağı meşru olarak /P, /Parent ya da /Annots adını taşıyabilir; /P girdisini yayılımdan atan bir /Resources sözlüğü, saldırganın sayfaya ait bir XObject'i masum bir kaynak adının ardına saklamasına izin verirdi. v3.126.2'de navigation filtresi yalnızca sahip sözlük gerçekten bir page, Pages node'u, annotation, widget ya da field olduğunda uygulanır. Bu sözlüklerden biri çiftlenmiş bir navigation anahtarı taşıyorsa — bir widget'ta iki /P girdisi gibi — çözümleyici, görüntüleyicinin hangi kopyayı kullanacağını tahmin etmez; rol kurulumu başarısız olur ve imza Indeterminate olur
Birkaç kural daha, kalan yeniden etiketleme yollarını kapatır:
- Pages node'ları kendi haklarıyla page-rol kökleridir; dolayısıyla Pages ağacından miras kalan kaynaklar (ISO 32000-1 §7.7.3.4) sayfa bağlamına gerçek sahiplikten girer, bir çocuk sayfadan
/Parentyürüyüşünden değil - Bir catalog, Pages node'u, page, annotation ya da field sözlüğüne ulaşan bir annotation ya da form rolü orada durur; çünkü o yapısal object'ler kendi rollerini kurarlar ve gelen bir payload rolü onları ezmemelidir
- Sınıflandırma sırasında page rolü yetkilidir: sayfaya ait bir object, sonraki bir revizyon onu sahte bir
/FTile ya da/Type /Annotetiketiyle yeniden yazsa ya da bir appearance stream'le paylaşsa bileprckPageContent'tir - Yalnızca bir field ya da annotation görünümü olarak kullanılan bir Form XObject, form ya da annotation kategorisini korur; dolayısıyla form doldurma sonrası sıradan görünüm yenilenmesi yine normal izin kuralları altında notlandırılır
- Kendi
/FT'si olmayan bir widget, miras kalan field tipini/Parentzinciri üzerinden çözer ve çözülemeyen bir zincir, annotation'a varsaymak yerine rol kurulumunu düşürür - Her sonraki revizyonun rol bitleri, kapsanan revizyonun rollerine birleştirilir; dolayısıyla sonraki bir update, önce bir stream'i koparıp sonra düzenleyerek önceki bir sayfa-sahiplik ilişkisini silemez
Sabit geçiş sayısı yerine sabit nokta
v3.126.2'de rol erişilebilirliği, hiçbir object yeni bir rol bit kazanmayana dek yineleyen bir iş kuyruğu olarak koşar; bu, zincir derinliğinden ya da object numaralandırmasından bağımsız gerçek bir sabit noktadır. Kendi object'i olarak saklanan bir /Contents dizisi gibi dolaylı diziler de yürünür. Her object en çok dört ayrı rol bit kazanabilir; dolayısıyla kuyruk, object numarası başına dört girdiyle sınırlıdır; o bütçeyi aşmak prrResourceLimitExceeded fırlatır. Serbest bir object'e referans, generation uyuşmazlığı ya da bozuk bir object header prrMalformedRevisionChain fırlatır; sıkıştırılmış bir object stream içindeki payload ise prrCompressedObjectUnresolved. Bu başarısızlıkların her biri prasIndeterminate ile biter, asla izinli bir hükümle değil; ve kapsanan revizyonun rolleri kurulurken başarısızlık olursa imza hiç Changes bildirmez
Aşağıdaki rutin, bu analizi atlatan sayfa-içerik düzenlemelerini listeler. Bir prckPageContent değişikliği asla prdAllowed notlandırılmaz: DocMDP P=1, 2 ya da 3 onu prdDisallowed yapar ve DocMDP'siz bir imza onu prdSuspicious notlandırır
uses
SysUtils, TypInfo, PDFium, FPdfPades;
procedure ListPageContentEdits(const FileName: string);
const
ShadowTag: array[Boolean] of string = (' (unreferenced shadow)', '');
var
Pdf: TPdf;
Report: TPadesRevisionAnalysisReport;
Sig: TPadesSignatureRevisionAnalysis;
Change: TPadesRevisionObjectChange;
i, j: Integer;
begin
Pdf := TPdf.Create(nil);
try
Pdf.FileName := FileName;
Pdf.Active := True;
Report := Pdf.AnalyzeSignatureRevisions; // record, serbest bırakılacak bir şey yok
for i := 0 to High(Report.Signatures) do
begin
Sig := Report.Signatures[i];
if not (prrPageContentChanged in Sig.Risks) then
Continue;
Writeln(Format('Signature %d (DocMDP P=%d): page content changed',
[Sig.SignatureIndex, Sig.DocMdpPermission]));
for j := 0 to High(Sig.Changes) do
begin
Change := Sig.Changes[j];
if Change.Kind <> prckPageContent then
Continue;
Writeln(Format(' revision %d object %d %d R %s%s',
[Change.RevisionIndex, Change.ObjectNumber, Change.Generation,
GetEnumName(TypeInfo(TPadesRevisionDecision), Ord(Change.Decision)),
ShadowTag[Change.IsAuthoritative]]));
end;
end;
finally
Pdf.Free;
end;
end;
FieldMDP ile bir annotation tek bir object'i paylaştığında ne olur?
Bir field'ın /V'si ile bir annotation'ın /Contents'i aynı dolaylı object'i gösterdiğinde v3.126.2, değişiklik bir annotation düzenlemesi diye sınıflansa bile FieldMDP kilidini yürürlükte tutar. Senaryoyu elle kurmak kolaydır: imzalayan, Total field'ını FieldMDP ile kilitler (ISO 32000-1 §12.8.2.4) ve saldırgan, bir metin annotation'ının /Contents'ini, field değerini tutan aynı string object'ine referans gösterir. P=3 altında annotation düzenlemesine izin verilir; dolayısıyla düzeltmeden önce o paylaşılan string'i yeniden yazmak, izinli bir hükmle kilitli bir field değerini değiştiriyordu
Object artık hem annotation hem form rolünü taşır ve imza bir FieldMDP transform taşıdığında annotation kararı form tarafını yeniden kontrol eder:
- P=2 altında annotation değişikliği tümüyle yasaktır; tıpkı öncesi gibi
- FieldMDP
Allile her field kilitlidir; dolayısıyla paylaşılan değişiklikprdDisallowed'dur - FieldMDP
Includeya daExcludeile çözümleyici, paylaşılan bir skaleri tek bir field adına geri izleyemez; dolayısıyla karar bir tahmin yerineprdIndeterminate'dir - FieldMDP yokken P=3 annotation kuralı uygulanır ve değişiklik izinli kalır
Kapı kodu için bir raporlama ayrıntısı önemlidir. Paylaşılan durum, Decision = prdIndeterminate ile Kind = prckAnnotation diye bildirilir ve prrFieldMdpUnresolved, yalnızca form field diye sınıflanan değişiklikler için risk kümesine eklenir. prrFieldMdpUnresolved'i arayıp Status'i yok sayan bir kapı bu durumu tümüyle kaçırır
Delphi kodu revizyon analizinde kapalıya nasıl düşmeli?
Delphi kodu, imzalı bir belgeyi yalnızca analiz durumu prasNoLaterChanges ya da prasAllowed olduğunda ve yapısal bir risk yokken kabul etmelidir; prasIndeterminate ile prasSuspicious'ı, loglanıp geçilecek uyarılar olarak değil güvenilmez saymalıdır. Indeterminate, çözümleyicinin sonraki revizyonların izinli olduğunu kanıtlayamadığı demektir; saldırgan için, kodunuz içeri alıyorsa kararlı biçimde Indeterminate üreten bir girdi, Allowed üreten bir girdi kadar işe yarar. Global AnalyzePadesSignatureRevisions herhangi bir TStream alır ve onu 0 konumundan okur; belgeyi asla render etmesi gerekmeyen upload handler'larına uyar
uses
Classes, SysUtils, TypInfo, FPdfPades;
const
// Çift tanımlar, Status düşürülmeden kaydedilir
BlockingRisks: TPadesRevisionRisks = [prrMalformedRevisionChain,
prrDuplicateObjectDefinition, prrUnreferencedObjectDefinition,
prrSignatureObjectRedefined, prrCompressedObjectUnresolved,
prrResourceLimitExceeded];
function SignedRevisionsAcceptable(const FileName: string;
out Reason: string): Boolean;
var
Source: TFileStream;
Report: TPadesRevisionAnalysisReport;
begin
Result := False;
Reason := '';
Source := TFileStream.Create(FileName, fmOpenRead or fmShareDenyWrite);
try
Report := AnalyzePadesSignatureRevisions(Source);
finally
Source.Free;
end;
if Report.SignatureCount = 0 then
begin
Reason := 'no signature anchors the analysis';
Exit;
end;
if Report.Risks * BlockingRisks <> [] then
begin
Reason := 'structural risk in the revision chain';
Exit;
end;
case Report.Status of
prasNoLaterChanges, prasAllowed:
Result := True;
else
// prasIndeterminate ile prasSuspicious reddediliştir, uyarı değil
Reason := 'revision status ' +
GetEnumName(TypeInfo(TPadesRevisionAnalysisStatus), Ord(Report.Status));
end;
end;
İki sınırı açıkça söylemeye değer. TPadesRevisionAnalysisReport, CMS bütünlüğü ya da sertifika güveni hakkında hiçbir şey söylemez; dolayısıyla bu kapı, kriptografik ve güven doğrulamasının yanında durur, yerine geçmez. Ve doğru bir sahiplik grafiği, P=3'ü her iş akışı için güvenli yapmaz. P=3 annotation'lara gerçekten izin verir ve opak görünümlü bir annotation, tek bir content stream'e dokunmadan imzalı metni kaplayabilir. Sertifikalı belgeleriniz inceleme kopyası değil sözleşmeysa ya P=2 ile sertifikalayın ya da izinli annotation değişikliklerini bir insana yönlendirin; şu yardımcıdaki gibi:
function AllowedAnnotationEditsUnderP3(
const Report: TPadesRevisionAnalysisReport): Integer;
var
i, j: Integer;
begin
Result := 0;
for i := 0 to High(Report.Signatures) do
if Report.Signatures[i].DocMdpPermission = 3 then
for j := 0 to High(Report.Signatures[i].Changes) do
if (Report.Signatures[i].Changes[j].Kind = prckAnnotation) and
(Report.Signatures[i].Changes[j].Decision = prdAllowed) then
Inc(Result);
end;
İmza revizyonu denetim listesi
Bu listeyi, doğrulama hattınızın açıkta kalıp kalmadığını ve artık kapalıya düşüp düşmediğini kontrol için kullanın:
- v3.126.2 öncesi PDFium Component derlemeleri, DocMDP P=3 belgelerindeki sayfa içerik düzenlemeleri için
prasAllowedbildirebiliyordu; eski derlemelerin kabul ettiği sertifikalı P=3 dosyalarındaTPdf.AnalyzeSignatureRevisions'i yeniden koşturun - Bir field değeri ile bir annotation'ın dolaylı bir object'i paylaşabileceği, FieldMDP kilitli P=3 belgelerini yeniden kontrol edin
- Yalnızca
prasNoLaterChangesileprasAllowed'ı kabul edin;prasIndeterminateileprasSuspicious'ı güvenilmez sayın Report.Status'in yanındaReport.Risks'i de test edin; çünküprrDuplicateObjectDefinitiontek başına durumu değiştirmez- İmza durumu Indeterminate iken boş bir
Changesdizisini temiz bir sonuç diye okumayın; başarısız bir rol kurulumu hiç değişiklik bildirmez - FieldMDP sorunlarını yakalamak için yalnızca
prrFieldMdpUnresolved'e bel bağlamayın; paylaşılan annotation durumu karar ve durum üzerinden görünür - P=3 altındaki izinli annotation değişikliklerinin iş akışınızda insan incelemesi gerekip gerekmediğine karar verin
- Özgün dosya baytlarını analiz edin;
SaveAsile yeniden yazılmış bir belge artık revizyon zincirini içermez
Revizyon analizi, bir imza kontrolünün bir katmanıdır. Sözlük ve baseline düzeyi için PDF dijital imzalarını ve PAdES düzeylerini inceleme ile; JavaScript, launch action'ları ve gömülü dosyalar için daha geniş bir PDF güvenlik riski denetimi ile eşleştirin. TPdf.AnalyzeSignatureRevisions, AnalyzePadesSignatureRevisions ve burada anlatılan sahiplik duyarlı rol grafiği, Delphi, C++Builder ve Lazarus için PDFium Component ile gelir