Technical Article

Сигурен преглед на PDF в Delphi приложения с PDFium Component

Прегледът на ненадежден PDF файл във вашето собствено приложение е оперативно решение и това, което има значение, не е интерфейсът (chrome) на визуализатора, а това, което панелът отказва да направи сам. Не записвайте файла на диска. Не позволявайте на неговите връзки да изпълняват външни команди. Не давайте път на неговите прикачени файлове. Повечето от щетите от зловреден документ идват не от експлойт в ядрото, а от визуализатор, изпълняващ напълно обичайни неща с въведени от нападателя данни: отваряне на file:// връзка към UNC споделен ресурс, която разкрива NTLM идентификационни данни, оставяне на копие в рамките на временната директория (temp), или копиране на вградени файлове навсякъде, където посочва името на файла. PDFium Component е PDF визуализатор с изходен код за Delphi, C++Builder и Lazarus и той поставя съответните превключватели там, където можете да ги достигнете: флаг при зареждане, който спира скриптовете, събития при кликване върху връзки, които можете да отхвърлите, достъп до прикачени файлове през ваш собствен код и четими битове за права. Редът по-долу следва документа от момента, в който той пристигне, до момента, в който потребителят кликне върху нещо в него.

Модел на заплахи за панела за предварителен преглед

Бъдете честни за това какво ви носи „сигурният прегледâ€? Рендиращият модул анализира ненадеждни байтове, независимо какво правите, а защитата на самото ядро е основата, върху която стъпвате. Всичко над тази основа е политика на приложението: дали скриптовете се инициират, какво прави кликването върху връзка, дали вградените файлове могат да достигнат до диска, дали клипбордът и принтерът са отворени врати или прегради. Едно нещо, което трябва да отпишете отрано, е превключвателят FPDF_SetSandBoxPolicy на ядрото. Повечето ограничения в него са компилирани директно, превключвателят променя малко в практиката, а разчитането на него за изолация създава фалшиво чувство на сигурност, че сте направили нещо. Когато входните данни са наистина опасни (например публичен портал за качване на файлове), единствената реална изолация е рендирането в отделен процес с ниски привилегии и изпращането на битмапи към потребителския интерфейс. Флаговете в рамките на процеса са просто политика. Те не са защитна бариера.

Две области са лесни за забравяне именно защото никое кликване не ги докосва директно. Първата е временните файлове. Ако вашият конвейер записва входящите документи на диска преди прегледа, тези временни копия остават и след края на сесията, освен ако нещо гарантирано не ги изтрие. Всеки файл, който може да се възстанови от временната директория, тихо заобикаля контрола, наложен от самия панел. Зареждайте файловете директно от паметта чрез TPdfStreamAdapter, така че потенциално опасните байтове никога да не получават свой собствен път на диска. Втората област е клипбордът. Предварителен преглед, който позволява маркиране и копиране, вече е изнесъл документа (екран по екран) и никакво прихващане на връзки няма да спре това.

Спиране на JavaScript при зареждане, а не в потребителския интерфейс

JavaScript в PDFium Component се инициира само заедно със средата за попълване на формуляри (form-fill). Следователно зареждането с FormFill := False изключва скриптовете в корена, вместо просто да потиска симптомите им:

procedure TPreviewPane.LoadUntrusted(const FilePath: string);

begin

  Pdf.FileName := FilePath;

  Pdf.FormFill := False;     // no form environment, hence no JavaScript engine

  Pdf.Active := True;



  FPermissions := Pdf.Permissions;   // raw flag word; all bits set = unrestricted

end;

Компромисът е реален и трябва да бъде част от вашата спецификация. При изключено попълване на формуляри легитимните AcroForm взаимодействия и скриптове за валидиране също изчезват. Полетата се рендират с последния си записан вид, но не могат да бъдат редактирани. За панел за предварителен преглед това обикновено е правилното решение, тъй като прегледът означава разглеждане, а не попълване. Но ако същият прозорец служи и за попълване на формуляри за доверени вътрешни документи, решението е два различни пътя за зареждане с изрично решение за доверие между тях, а не един компромисен път, който е твърде хлабав за опасния случай и твърде строг за доверения. Страната на попълването на формуляри има свои собствени клопки, разгледани в материала за навигация в полетата на формуляри и регенериране на външния вид.

Връзки: стандартният манипулатор изпълнява външни команди

Ако бъдат оставени без контрол, кликванията върху връзки отиват директно към операционната система. Стандартните LinkOptions на визуализатора включват loAutoOpenURI, което е предпоставка за изтичане на данни от тип file:// към UNC ресурси. Две събития представляват критичната точка: OnWebLinkClick за URL адреси, открити в текста на страницата, и OnAnnotationLinkClick за анотации на връзки, носещи URI или действия за стартиране. Задайте безкомпромисно Handled := True и в двете събития, преди да вземете каквото и да е решение, и след това разрешете само това, което политиката позволява. Като втори защитен слой премахнете loAutoOpenURI от LinkOptions за опасни входни данни и се уверете, че опцията loAutoLaunch, която е изключена по подразбиране, никога не се активира отново чрез копиране на конфигурации:

procedure TPreviewPane.PdfViewWebLinkClick(Sender: TObject;

  const Url: WString; var Handled: Boolean);

begin

  Handled := True;   // never fall through to the default shell behavior



  if (AnsiStartsText('https://', Url) or AnsiStartsText('http://', Url))

    and HostIsAllowed(Url) then

    OpenInBrowser(Url)

  else

    FAudit.LogBlockedLink(FDocumentId, Url);

end;

Две подробности определят дали това наистина работи. Първо, проверката на схемата на адреса трябва да бъде проверка на префикса на необработения низ преди всякакъв анализ, тъй като file://, UNC пътища и екзотични схеми са точно тези стойности, които сриват обикновен URL анализатор или преминават през такъв, който нормализира низовете твърде агресивно. Второ, записвайте всяко блокиране с приложена идентичност на документа. Няколко блокирани file:// връзки са просто фонов шум, но вълна от тях в множество входящи документи за кратък период от време е инцидент, за който вашият екип по сигурността би искал да научи веднага.

Прикачени файлове: политика за разширенията и името на файла, което не сте избрали

PDF файлът е контейнер и свойствата AttachmentCount и AttachmentName[] ви казват какво съдържа той, преди нещо да докосне диска. Тук са важни два отделни контролни механизма и само единият от тях е очевиден. Очевидният е политиката за типовете файлове: бял списък (allowlist) с разширения, които могат да бъдат експортирани. По-деликатният момент е, че името на прикачения файл е изцяло контролирано от нападателя. Вградено име как ..\..\Startup\update.exe превръща небрежното запазване в уязвимост от тип „преминаване през директорииâ€?(path traversal), която записва изпълним файл в папка, стартирана от Windows при влизане в системата. Компонентът ви предоставя вградените данни как байтове чрез Attachment[] и оставя вашият код да избере пътя за запис. Затова изграждайте този път от изчистено базово име на файл, а никога от оригиналния вграден низ:

procedure TPreviewPane.ExportAttachment(Index: Integer; const TargetDir: string);

var

  RawName, SafeName, Ext: string;

  Data: TBytes;

begin

  RawName := string(Pdf.AttachmentName[Index]);

  SafeName := ExtractFileName(RawName);    // strips any path components

  Ext := LowerCase(ExtractFileExt(SafeName));



  if not FAllowedExt.Contains(Ext) then    // allowlist, not blocklist

    raise EPreviewPolicy.CreateFmt('Attachment type %s blocked by policy', [Ext]);



  Data := Pdf.Attachment[Index];           // embedded payload as raw bytes

  TFile.WriteAllBytes(

    IncludeTrailingPathDelimiter(TargetDir) + SafeName, Data);

end;

Предпочитайте подхода с бял списък. Черният списък с „опасниâ€?разширения е състезание, което ще загубите в деня, в който някой използва разширение, за което никога не сте чували. Белият списък с .pdf, .png и .csv гарантира сигурност по подразбиране.

Какво всъщност гарантират правата за шифроване

Стандартният модул за сигурност на ISO 32000-1 кодира флагове за права за печат, копиране на съдържание и модификация. Свойствата Permissions и UserPermissions ги показват като необработени битови маски след отварянето на документа. Таблица 22 от ISO 32000-1 дефинира тези битове, а нешифрованият файл докладва, че всички битове са зададени. Прочитайте ги и ги спазвайте на ниво команди във вашето приложение, но бъдете наясно какво всъщност представляват те. При документ, шифрован с парола на собственика (owner password) и празна потребителска парола, съдържанието се дешифрира напълно при отваряне. Тези флагове са просто молба към съвместимите визуализатори, а не технически наложен механизъм за защита. Това има две последствия, които водят в противоположни посоки. Първо, никога не представяйте тези права на потребителите като реално свойство за сигурност на документите, тъй като те не са такова. Второ, спазвайте бита за извличане за достъпност (бит 10), дори когато общото копиране (бит 5) е забранено. Достъпът за екранни четци е отделен умишлено в модела на правата и премахването му, само защото „копирането е изключеноâ€? нарушава работата на асистиращите технологии без реална полза за сигурността.

Налагайте забранените действия на ниво команда, а не чрез скриване на бутони от лентата с инструменти. Клавишната комбинация Ctrl+C, контекстните менюта и маркирането с влачене лесно заобикалят бутоните на лентата с инструменти. Една проверка на правата вътре в самата команда за копиране обаче не може да бъде заобиколена.

За документи, които изискват потребителска парола, задайте свойството Password преди Active := True и се отнасяйте към стойността като към тайна: извличайте я от вашето хранилище за идентификационни данни за всяка сесия, дръжте я далеч от логове и отчети за сривове и никога не я запазвайте до самия документ. Панел за предварителен преглед, който кешира пароли „зÐ?удобствоâ€? тихомълком се превръща в база данни за пароли без съответните защити.

Печатът заслужава собствено решение, а не просто да наследява правилото за копиране на съдържание. Физическото разпечатване по дефиниция не подлежи на лесен одит, но пълната му забрана често кара потребителите да правят екранни снимки (screenshots), което е по-лошо по всички показатели. Често използван среден вариант е да се позволи печатът, но всяка страница да се подпечатва с идентичността на потребителя и времеви печат, което се налага вътре в самата команда за печат. Имайте реални очаквания: водният знак е средство за възпиране и проследяване на авторството. Той не е техническа защита срещу разпространение.

Какво процесът на входяща проверка вече трябва да ви е казал

Панелът за предварителен преглед взема по-добри решения, когато файлът пристигне с предварително изготвено досие: дали е шифрован, дали съдържа JavaScript, опис на прикачените файлове и типа на формуляра. Този анализ трябва да се извършва преди визуализатора, а моделът в изграждането на работна среда за преглед на входящи PDF файлове генерира точно тези флагове, които политиката за преглед трябва да използва. Файлове, които входящата проверка е маркирала като рискови, автоматично се отварят през защитения път, докато стандартните документи запазват удобствата си. Свържете двата етапа към един общ обект на политиката, вместо към два отделни конфигурационни екрана, които ще се разминат още при втората версия на софтуера.

Къде точно преминава границата между изпълнението в рамките на процеса (in-process) и извън него (out-of-process) зависи от това кой ви изпраща файловете. За обичайния входящ бизнес поток изпращачите на документи са известни и най-много да са небрежни. В този случай прегледът в рамките на процеса с изключени скриптове и прихванати връзки е приемлива защита. За анонимни публични качвания това не е така и никакви настройки на флагове в процеса няма да помогнат. Рендирайте тези документи в отделен процес с ниски привилегии и изпращайте битмапи към потребителския интерфейс, така че дефект в ядрото да ви коства само този процес, а не цялото хост приложение. Вземете това решение съзнателно и класифицирайте всеки входящ канал, тъй като цената на грешната преценка е твърде висока.

Лицензирането, свързаният със сигурността API и демонстрацията на защитен визуализатор са достъпни на продуктовата страница: PDFium Component.