مقاله فنی

پیش‌نمایش امن PDF در برنامه‌های دلفی با کامپوننت PDFium

پیش‌نمایش یک PDF غیرقابل اعتماد درون برنامه خود شما یک تصمیم اجرایی است و بخشی که اهمیت دارد، ظاهر بیننده نیست بلکه کاری است که پنل از انجام آن خودداری می‌کند. فایل را روی دیسک ننویسید. اجازه ندهید لینک‌های آن به پوسته (shell) متصل شوند. مسیری به پیوست‌های آن ندهید. بیشتر آسیب‌های یک سند مخرب از اکسپلویت کردن موتور ناشی نمی‌شود، بلکه از بیننده‌ای ناشی می‌شود که کارهای کاملاً معمولی را با ورودی تهیه‌شده توسط مهاجم انجام می‌دهد: باز کردن یک لینک file:// به یک اشتراک‌گذاری UNC که اعتبارنامه‌های NTLM را به بیرون درز می‌دهد، رها کردن یک نسخه مرحله‌بندی‌شده در پوشه موقت، کپی کردن محموله‌های تعبیه‌شده به هر جایی که یک رشته نام فایل به آن می‌گوید. کامپوننت PDFium یک بیننده سورس‌کد PDF برای Delphi، C++Builder و Lazarus است و سوییچ‌های مربوطه را در جایی قرار می‌دهد که می‌توانید به آنها دسترسی داشته باشید: یک پرچم در زمان بارگذاری که اسکریپت‌نویسی را از بین می‌برد، رویدادهای کلیک روی لینک که می‌توانید آنها را وتو کنید، دسترسی به پیوست که از طریق کد خود شما اجرا می‌شود و بیت‌های مجوز که می‌توانید آنها را بخوانید. ترتیب زیر، یک سند را از لحظه ورود تا لحظه‌ای که کاربر روی چیزی در آن کلیک می‌کند، دنبال می‌کند

مدل تهدید یک پنل پیش‌نمایش

صادق باشید که «پیش‌نمایش امن» چه چیزی برای شما می‌خرد. رندرکننده بایت‌های غیرقابل اعتماد را بدون توجه به کاری که انجام می‌دهید تجزیه می‌کند و سخت‌کردن خود موتور، کفی است که روی آن می‌ایستید. هر چیزی بالاتر از آن کف، خط‌مشی برنامه است: این که آیا اسکریپت‌ها راه‌اندازی می‌شوند، کلیک روی یک لینک چه کاری انجام می‌دهد، آیا فایل‌های تعبیه‌شده می‌توانند به دیسک برسند، آیا کلیپ‌بورد و چاپگر در هستند یا دیوار. یک چیز که باید زودتر کنار گذاشته شود سوییچ FPDF_SetSandBoxPolicy موتور است. بیشتر محدودیت‌های موتور در آن کامپایل شده‌اند، سوییچ در عمل تغییر کمی ایجاد می‌کند و اختصاص هر بخشی از داستان ایزوله‌سازی شما به آن، فقط حس کاذبی از انجام یک کار ایجاد می‌کند. وقتی ورودی واقعاً مخرب است، مثلاً یک پورتال بارگذاری عمومی، تنها ایزوله‌سازی واقعی، رندر کردن در یک فرآیند جداگانه با امتیاز پایین و ارسال بیت‌مپ‌ها به رابط کاربری است. پرچم‌های درون‌فرآیندی خط‌مشی هستند. آنها مهار کردن نیستند

فراموش کردن دو سطح دقیقاً به این دلیل آسان است که هیچ کلیکی هرگز آنها را لمس نمی‌کند. اولی فایل‌های موقت است. اگر خط لوله شما اسناد ورودی را قبل از پیش‌نمایش روی دیسک مرحله‌بندی می‌کند، آن نسخه‌های مرحله‌بندی‌شده بیشتر از جلسه زنده می‌مانند مگر اینکه چیزی آنها را به‌طور قابل تاییدی حذف کند، و فایلی که «از دایرکتوری موقت قابل بازیابی است» در سکوت هر کنترلی را که خود پنل اعمال می‌کند، شکست داده است. در عوض، با استفاده از TPdfStreamAdapter از حافظه بارگذاری کنید تا بایت‌های مخرب هرگز مسیری برای خودشان نداشته باشند. دومی کلیپ‌بورد است. پیش‌نمایشی که امکان انتخاب و کپی را فراهم می‌کند، قبلاً سند را یک صفحه در یک زمان صادر کرده است و هیچ رهگیری لینکی متوجه آن نخواهد شد

جاوا اسکریپت را در زمان بارگذاری بکشید، نه در رابط کاربری

جاوا اسکریپت سند در کامپوننت PDFium فقط همراه با محیط پر کردن فرم راه‌اندازی می‌شود. بنابراین، بارگذاری با 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 و اسکریپت‌های اعتبارسنجی نیز از بین می‌روند؛ فیلدها با آخرین ظاهر ذخیره‌شده خود رندر می‌شوند اما قابل ویرایش نیستند. برای یک پنل پیش‌نمایش، این معمولاً تماس درستی است، زیرا پیش‌نمایش یعنی نگاه کردن، نه پر کردن. اما اگر همان پنجره به عنوان سطح پر کردن فرم برای اسناد داخلی قابل اعتماد نیز کار کند، پاسخ، دو مسیر بارگذاری با یک تصمیم اعتماد صریح بین آنهاست، نه یک مسیر با تنظیم سازش که برای مورد مخرب بسیار شل و برای مورد قابل اعتماد بسیار سفت است. سمت پر کردن فرمِ آن تقسیم تله‌های خاص خود را دارد که در ناوبری فیلد فرم و بازسازی ظاهر پوشش داده شده است

لینک‌ها: کنترل‌کننده پیش‌فرض به پوسته متصل می‌شود

اگر به حال خود رها شوند، کلیک‌های لینک مستقیماً به سیستم‌عامل می‌روند. گزینه‌های پیش‌فرض بیننده برای لینک شامل 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[] قبل از اینکه هر چیزی با دیسک برخورد کند، به شما می‌گویند چه چیزی در آن قرار دارد. در اینجا دو کنترل مجزا اهمیت دارند، و فقط یکی از آنها واضح است. مورد واضح، خط‌مشی نوع است: یک لیست مجاز از پسوندهایی که ممکن است صادر شوند. مورد ظریف این است که نام پیوست، داده‌هایی است که توسط مهاجم کنترل می‌شود، و تمام. یک نام تعبیه‌شده مانند ..\..\Startup\update.exe یک ذخیره بی‌دقت را به یک پیمایش مسیر تبدیل می‌کند که یک فایل اجرایی را در پوشه‌ای که ویندوز در هنگام ورود اجرا می‌کند، رها می‌کند. این کامپوننت محموله را به عنوان بایت‌ها از طریق 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 پس از باز شدن سند، آن‌ها را به‌عنوان نقاب‌های بیت خام نشان می‌دهند. جدول ۲۲ ایزو 32000-1 بیت‌ها را تعریف می‌کند، و یک فایل رمزگذاری‌نشده هر بیت تنظیم‌شده‌ای را گزارش می‌دهد. آنها را در لایه دستورات خود بخوانید و رعایت کنید، اما مشخص کنید که چه هستند. برای سندی که با رمز عبور مالک و رمز عبور خالی کاربر رمزگذاری شده است، محتوا در هنگام باز شدن کاملاً رمزگشایی می‌شود، و پرچم‌ها یک درخواست به بینندگان مطابق هستند، نه یک مکانیسم اجرایی. این دو پیامد دارد و در جهت مخالف یکدیگر کشیده می‌شوند. هرگز پرچم‌های مجوز را به‌عنوان یک ویژگی امنیتی برای اسنادی که کاربران دریافت می‌کنند به آنها ارائه ندهید، زیرا اینگونه نیستند. در عین حال، به بیت استخراج دسترسی (بیت ۱۰) حتی در جایی که کپی عمومی (بیت ۵) رد می‌شود، احترام بگذارید؛ دسترسی صفحه‌خوان به‌طور هدفمند به‌طور جداگانه در مدل مجوز حک شده است و حذف آن به دلیل "خاموش بودن کپی" فناوری کمکی را بدون هیچ دستاورد امنیتی می‌شکند

اعمال ردشده را در سطح دستور اجرا کنید، نه با پنهان کردن دکمه‌های نوار ابزار. Ctrl+C، منوهای زمینه و کشیدن-انتخاب همه نوار ابزار را دور می‌زنند؛ یک بررسی تکی مجوز در داخل دستور کپی هیچ‌چیز را دور نمی‌زند

برای اسنادی که نیاز به رمز عبور کاربر دارند، قبل از Active := True، Password را تخصیص دهید و با مقدار آن مانند رازی که هست رفتار کنید: آن را به ازای هر نشست از فروشگاه اعتبار خود واکشی کنید، آن را دور از گزارشات لاگ و خرابی نگه دارید، و هرگز آن را در کنار سند نگه ندارید. یک پنل پیش‌نمایش که کلمات عبور را "برای راحتی" ذخیره می‌کند، بی‌سروصدا تبدیل به یک پایگاه داده رمز عبور با هیچ‌یک از محافظت‌های آن شده است

چاپ سزاوار تصمیم خود است تا اینکه وارث هر چیزی باشد که در قاعده کپی تنظیم شده است. یک چاپ فیزیکی از نظر تعریف بررسی‌نشده است، با این حال مسدود کردن کامل چاپ تمایل به سوق دادن کاربران به سمت عکس گرفتن از صفحه‌نمایش دارد که در هر محوری بدتر است. زمینه‌وسط مشترک، اجازه دادن به چاپ است اما مهر زدن هویت کاربر و مهر زمانی در هر صفحه که در داخل دستور چاپ اجرا می‌شود. فقط انتظار درستی از آن داشته باشید: یک واترمارک بازدارندگی و انتساب است. این یک پیشگیری نیست

ورودی چه چیزی باید به شما می‌گفت

یک پنل پیش‌نمایش زمانی که فایل با پرونده‌ای که قبلاً ضمیمه شده است، تصمیمات بهتری می‌گیرد: رمزگذاری‌شده یا نه، وجود یا عدم وجود جاوا اسکریپت، سرشماری پیوست‌ها، نوع فرم. این عبور بازرسی متعلق به بالادست بیننده است و الگو در ساخت یک میز کار بررسی ورودی PDF دقیقاً پرچم‌هایی را که یک سیاست پیش‌نمایش می‌خواهد مصرف کند، تولید می‌کند. فایل‌هایی که در هنگام ورود به عنوان پرخطر علامت‌گذاری شده‌اند، به طور خودکار از طریق مسیر سخت‌شده باز می‌شوند؛ اسناد معمولی راحتی خود را حفظ می‌کنند. هر دو مرحله را به یک شیء سیاست مشترک به جای دو صفحه نمایش پیکربندی مرتبط کنید، که مهم نیست برای بار اول چقدر با دقت آنها را بنویسید، در نسخه دوم از یکدیگر فاصله خواهند گرفت

جایی که مرز بین درون‌فرآیندی و برون‌فرآیندی قرار می‌گیرد به این بستگی دارد که چه کسی فایل‌ها را برای شما می‌فرستد. برای ورودی تجاری معمول، افرادی که اسناد را ارسال می‌کنند شناخته‌شده و صرفاً بی‌دقت هستند و پیش‌نمایش درون‌فرآیندی با غیرفعال بودن اسکریپت‌نویسی و رهگیری شدن لینک‌ها، یک میله قابل‌دفاع است. اما برای بارگذاری‌های عمومی و ناشناس اینگونه نیست، و هیچ مقدار تنظیم پرچم‌های درون‌فرآیندی، آن را به یکی تبدیل نمی‌کند؛ آنها را در یک کارگر با امتیاز پایین و مجزا رندر کنید و بیت‌مپ‌ها را به رابط کاربری بفرستید، به طوری که یک نقص موتور برای شما به قیمت یک کارگر تمام شود نه برنامه میزبان. این تفکیک را عمداً تصمیم‌گیری کنید و بنویسید که هر مسیر پردازش در کدام سطل قرار می‌گیرد، زیرا هزینه حدس اشتباه نامتقارن است

صدور مجوز، سطح API مرتبط با امنیت، و یک پیش‌نمایش تستی سخت‌شده در صفحه محصول موجود است: کامپوننت PDFium