Teknisk artikkel

Automatisert PDF-preflight og risiko-revisjon med PDFium

\n

Når bedriftssystemet ditt aksepterer PDF-er fra upålitelige eksterne kilder, er det å behandle hver innkommende fil identisk en oppskrift på ødelagte arbeidsflyter, sikkerhetshendelser og dårlig samsvar med tilgjengelighet. Dokumenter kan inneholde ondsinnede skript, mangle innebygde skrifttyper som kreves for utskrift, eller bestå utelukkende av skannede bilder som forhindrer tekstekstrahering

For å trygt innta dokumenter, trenger du en automatisert inntaksrevisjon og preflight-fase. I denne artikkelen ser vi på hvordan du bygger en robust revisjonsrørledning ved hjelp av Delphi og PDFium-motoren for å klassifisere, validere og rute innkommende PDF-er

Funnposten og kontrakten for avslutningskoder

En inntaksrevisjon skiller seg fra gjengivelse. Det er en rask, hodeløs prosess som skanner dokumentets interne metadata, ordbøker og sidestrukturer for å generere en risikoprofil. Basert på denne profilen utleder systemet en "anbefalt handling" for filen

En robust revisjonsrørledning bør se etter:

  • Manglende skrifttypeinnbygging: Dokumenter som mangler innebygde skrifttyper, kan gjengis perfekt på oppretterens maskin, men ødelegges under utskrift på serversiden
  • Skannede versus tekstsider: Hvis sidene bare inneholder store bilder og ingen tekstobjekter, må de rutes til en OCR-kø (Optical Character Recognition)
  • Samsvar med tilgjengelighet: Mangler skjemafelt verktøytips? Mangler dokumentet et logisk strukturtre (Tagged PDF)? Disse krever utbedring før publisering på nettet
  • Aktivt innhold: Eksterne URI-handlinger, innebygd JavaScript eller funksjoner som ikke støttes utgjør sikkerhetsrisikoer og bør utløse en karanteneprotokoll

Interaktive elementer: skript, startmål og eksterne lenker

Et vanlig anti-mønster i revisjonsverktøy er ganske enkelt å dumpe en massiv JSON-fil med tekniske brudd (for eksempel "Warning: XRef stream malformed", "Info: Has JavaScript"). Mens det er nyttig for feilsøking, er dette forferdelig for automatisert batch-prosessering

I stedet bør revisjonen utlede en anbefalt handling basert på et prioritert hierarki. For eksempel:

  1. Hvis dokumentet kaster et unntak ved innlasting eller har funksjoner som ikke støttes, er handlingen quarantine
  2. Hvis dokumentet krever et passord for å åpne, er handlingen request-password
  3. Hvis dokumentet er en ren skanning, er handlingen run-ocr
  4. Hvis dokumentet mangler tilgjengelighetstagger, er handlingen remediate-tags
  5. Hvis det ikke finnes noen store brudd, er handlingen pass-through

Ved å håndheve dette konservative hierarkiet, kan back-end-tjenestene dine ganske enkelt bytte på strengen for anbefalt handling for å bestemme hvilken mikrotjeneste som håndterer filen neste gang

Ressursmålinger: effektiv bilde-DPI

Fordi inntaksrørledningen må behandle tusenvis av dokumenter, må revisjonslogikken være strengt hodeløs. Den bør ikke instansiere noen UI-komponenter, visningstilstander eller kreve en aktiv skjermkontekst

For hvert bilde bør revisjonen beregne effektiv DPI fra pikselmålene og bildets størrelse på siden. Det fanger opp skannede bilder som ser skarpe ut i en visning, men blir uegnede for utskrift

Sikkerhetstilstand: kryptering og tillatelsesbiter

Les krypteringsordboken og tillatelsesbitene som en egen kontroll. Et passord som kan åpne filen sier ikke at utskrift, kopiering eller endring er tillatt for arbeidsflyten som skal bruke den

Standardmarkører: lese et PDF/A-krav

En PDF/A-markør i metadata er et krav som må kontrolleres, ikke et bevis på samsvar. Registrer påstanden separat slik at den kan sammenholdes med en faktisk validator senere

En kjøring mot en problemfil

En testfil bør gi flere funn samtidig, slik at rapporten viser at kontrollene kan kombineres og at avslutningskoden beholder samme betydning gjennom hele batchen

Hva denne revisjonen ikke kan fortelle deg

En PDFium-revisjon kan lese struktur, metadata og risikosignaler, men beviser ikke visuell kvalitet, full PDF/A-samsvar eller at et eksternt mål er trygt. Slike påstander krever egne validerings- og policytrinn

Til slutt, sørg for at utdataformatet ditt er konsistent. Enten du genererer en enkeltfil JSON-rapport eller en batch-aggregert CSV, må utdataskjemaet speile den interne revisjonsposten perfekt. Dette sikrer at automatiseringsskript nedstrøms aldri støter på semantisk glidning

Merk: Omfattende inntaksrevisjon og tilgjengelighetssjekker er innebygde funksjoner i PDFium Component

\n

Flere kodeeksempler

uses
  System.SysUtils, pdfium_lib;

procedure AuditPdfSecurity(const FileName: string);
var
  Doc: FPDF_DOCUMENT;
  PageCount, i, j: Integer;
  Page: FPDF_PAGE;
  AnnotCount: Integer;
  Annot: FPDF_ANNOTATION;
  AnnotSubType: FPDF_ANNOTATION_SUBTYPE;
  Action: FPDF_ACTION;
  ActionType: ULONG;
begin
  FPDF_InitLibrary();
  try
    // Load the document without a password
    Doc := FPDF_LoadDocument(PAnsiChar(AnsiString(FileName)), nil);
    if Doc = nil then
      raise Exception.Create('Failed to load document or password required.');

    try
      PageCount := FPDF_GetPageCount(Doc);
      Writeln(Format('Auditing %d pages...', [PageCount]));

      for i := 0 to PageCount - 1 do
      begin
        Page := FPDF_LoadPage(Doc, i);
        if Page <> nil then
        begin
          AnnotCount := FPDFPage_GetAnnotCount(Page);
          for j := 0 to AnnotCount - 1 do
          begin
            Annot := FPDFPage_GetAnnot(Page, j);
            if Annot <> nil then
            begin
              AnnotSubType := FPDFAnnot_GetSubtype(Annot);

              // Check for Link Annotations that might have malicious actions
              if AnnotSubType = FPDF_ANNOT_LINK then
              begin
                Action := FPDFAnnot_GetLinkAction(Annot);
                if Action <> nil then
                begin
                  ActionType := FPDFAction_GetType(Action);
                  // 2 represents PDFACTION_URI, 3 represents PDFACTION_SOUND, 4 represents PDFACTION_MOVIE
                  // We specifically look out for PDFACTION_LAUNCH or PDFACTION_JAVA_SCRIPT
                  if (ActionType = PDFACTION_JAVA_SCRIPT) or (ActionType = PDFACTION_LAUNCH) then
                  begin
                    Writeln(Format('WARNING: Malicious action detected on page %d', [i + 1]));
                  end;
                end;
              end;
              FPDFPage_CloseAnnot(Annot);
            end;
          end;
          FPDF_ClosePage(Page);
        end;
      end;
    finally
      FPDF_CloseDocument(Doc);
    end;
  finally
    FPDF_DestroyLibrary();
  end;
end;
procedure AuditPageImages(Page: FPDF_PAGE);
var
  ObjCount, i: Integer;
  PageObj: FPDF_PAGEOBJECT;
  ImgWidth, ImgHeight: Integer;
begin
  ObjCount := FPDFPage_CountObjects(Page);
  for i := 0 to ObjCount - 1 do
  begin
    PageObj := FPDFPage_GetObject(Page, i);
    if FPDFPageObj_GetType(PageObj) = FPDF_PAGEOBJ_IMAGE then
    begin
      FPDFImageObj_GetBitmap(PageObj); // Returns an FPDF_BITMAP you can inspect
      // Retrieve metadata using FPDFImageObj_GetImageMetadata
      // Calculate effective DPI based on quad coordinates
    end;
  end;
end;
\n