مقاله فنی

حسابرسی خطرات امنیتی PDF با PDFium در دلفی

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

این مقاله یک حسابرسی ایستا (static) و فقط‌خواندنی (read-only) روی آن سطح خطر را با استفاده از کامپوننت PDFium برای دلفی و لازاروس بررسی می‌کند. این حسابرسی هرگز صفحه‌ای را نقاشی نمی‌کند. این فرایند ساختار سند را تجزیه می‌کند، بخش‌هایی از فایل را که حامل رفتار هستند برمی‌شمارد، و یک گزارش ساده می‌نویسد. این تفاوت بین آن است که از یک غریبه بخواهید جیب‌هایش را در دم در خالی کند و یا اینکه به دلیل لبخند زدن به او اعتماد کنید

حسابرسی چیست، و چه چیزی نیست

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

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

var
  Pdf: TPdf;
begin
  Pdf := TPdf.Create(nil);
  try
    Pdf.FileName := 'Incoming_Invoice.pdf';
    Pdf.Active := True;          // parses structure, renders nothing
    // audit the loaded document here
  finally
    Pdf.Free;
  end;
end;

جاوا اسکریپت سند در درخت نام

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

یک حسابرس درباره هر یک از این اسکریپت‌ها به دو واقعیت نیاز دارد: اینکه وجود دارد، و اینکه چه چیزی در آن است. کامپوننت تعداد را در معرض دید قرار می‌دهد و به شما اجازه می‌دهد هر اقدام را به عنوان رکوردی حاوی نام اسکریپت و بدنه کامل آن بخوانید. خواندن بدنه اهمیت دارد. اسکریپتی با نام Doc.0 چیزی به شما نمی‌گوید، اما متن آن ممکن است app.launchURL را فراخوانی کند یا رشته‌ای را مونتاژ کرده و آن را به جایی بفرستد که نباید برود. استخراج کد منبع به گونه‌ای که یک بازرس بتواند آن را بخواند، تمام هدفِ پرچم‌گذاری فایلی است که کد را هنگام باز شدن اجرا می‌کند

var
  I: Integer;
  Action: TPdfJavaScriptAction;
begin
  if Pdf.JavaScriptActionCount > 0 then
    WriteLn('WARNING: document runs ', Pdf.JavaScriptActionCount,
            ' script(s) on open');
  for I := 0 to Pdf.JavaScriptActionCount - 1 do
  begin
    Action := Pdf.JavaScriptAction[I];
    WriteLn('  script "', Action.Name, '":');
    WriteLn(Action.Script);   // full body, for a human to read
  end;
end;

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

اقدامات راه‌اندازی (Launch) و URI

رفتار بعدی برای فهرست کردن روی لینک‌ها و یادداشت‌ها (annotations) زندگی می‌کند. دو نوع اقدام بیشترین اهمیت را برای یک حسابرس دارند. یک اقدام راه‌اندازی (Launch) یک برنامه خارجی را شروع می‌کند یا یک فایل محلی را هنگام اجرای لینک باز می‌کند. یک اقدام URI یک هدف وب را باز می‌کند. یک بازرس که به یک سند مشکوک نگاه می‌کند باید بتواند ببیند، بدون اینکه روی چیزی کلیک کند، که یک دکمه در صفحه سه برای راه‌اندازی cmd.exe یا باز کردن یک URL که با برند روی صفحه مطابقت ندارد، سیم‌کشی شده است

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

کنترل نمایشگری که اسناد را رندر می‌کند، جایی است که پیروی از یک اقدام در آن اتفاق می‌افتد، و وضعیت پیش‌فرض آن عمداً محتاطانه است. کنترل TPdfView دارای مجموعه‌ای از LinkOptions است که تصمیم می‌گیرد کدام انواع لینک به طور خودکار با یک کلیک شلیک شوند. پیش‌فرض آن [loAutoGoto, loAutoOpenURI] است، به این معنی که پرش‌های داخل سند و URL‌های وب ممکن است باز شوند، اما loAutoLaunch غایب است، بنابراین اقدامات راه‌اندازی هرگز به طور خودکار اجرا نمی‌شوند. برای یک گردش کار حسابرسی شما فراتر می‌روید و مجموعه را کاملاً پاک می‌کنید، بنابراین در حالی که هنوز در حال تصمیم‌گیری برای اعتماد به فایل هستید، هیچ چیزی به طور خودکار شلیک نمی‌شود

// Audit posture for the viewer: nothing auto-runs, nothing auto-opens.
View.LinkOptions := [];

// The shipped default already withholds launch:
//   default = [loAutoGoto, loAutoOpenURI]
//   loAutoLaunch is NOT in the default set, so external programs
//   are never started on a stray click out of the box.

استدلال پشت خودداری از راه‌اندازی به طور پیش‌فرض ساده است. یک پرش در داخل سند بی‌ضرر است و یک URL قابل مشاهده و قابل لغو است، اما شروع یک برنامه خارجی دلخواه از یک کلیک خطرناک‌ترین چیزی است که یک لینک PDF می‌تواند درخواست کند، بنابراین خاموش است مگر اینکه شما آن را فعال کنید (opt in). یک حسابرس حتی از رفتارهای ایمن هم انصراف می‌دهد، زیرا کار نگاه کردن است، نه عمل کردن

سطح مجوز MDP امضای دیجیتال

امضاها سوال را تغییر می‌دهند. یک امضای ساده به بایت‌ها در زمان امضا گواهی می‌دهد. یک امضای گواهی‌نامه (certification)، از نوعی که با یک قانون تشخیص و پیشگیری از تغییر سند (MDP) ایجاد می‌شود، فراتر می‌رود: اعلام می‌کند که چه چیزی ممکن است به طور قانونی پس از گواهی شدن سند تغییر کند، و یک نمایشگر سازگار هشدار می‌دهد اگر چیزی خارج از آن مجاز تغییر کرده باشد. خواندن آن سطح مجوز به یک حسابرس می‌گوید که آیا فایل گواهی شده است یا خیر و در این صورت، قرار است چقدر قفل شده باشد

مجوز MDP یک عدد صحیح با سه مقدار تعریف شده است. سطح 1 به این معنی است که هیچ تغییری به هیچ وجه مجاز نیست؛ هرگونه اصلاح، گواهی را نقض می‌کند. سطح 2 اجازه پر کردن فرم و امضا را می‌دهد، حالت رایج برای قراردادی که قرار است تکمیل و امضا شود اما غیر از آن تغییر نکند. سطح 3 علاوه بر پر کردن فرم و امضا، اجازه حاشیه‌نویسی (annotations) را نیز می‌دهد. دانستن سطح به منطق دریافت شما اجازه می‌دهد درباره نیت استدلال کنید: سندی که در سطح 1 گواهی شده و با این حال دارای فیلدهای فرم یا اسکریپت است، با خود تناقض دارد، و ارزش پرچم‌گذاری آن تناقض را دارد

کامپوننت تعداد امضاها را می‌خواند و هر یک را به عنوان رکوردی که فیلد Permission آن مقدار MDP را حمل می‌کند، که مستقیماً از فراخوانی زیرین FPDFSignatureObj_GetDocMDPPermission پر شده است، در معرض دید قرار می‌دهد. مجوز صفر به این معنی است که امضا یک امضای گواهی‌نامه (DocMDP) نیست، بنابراین هیچ قفل‌گذاری در سطح سند برای گزارش وجود ندارد

var
  I: Integer;
  Sig: TPdfSignature;
begin
  if Pdf.SignatureCount = 0 then
    WriteLn('document is not signed')
  else
    for I := 0 to Pdf.SignatureCount - 1 do
    begin
      Sig := Pdf.Signature[I];
      case Sig.Permission of
        1: WriteLn('certified: no changes allowed');
        2: WriteLn('certified: form fill and signing allowed');
        3: WriteLn('certified: form fill, signing and annotations allowed');
      else
        WriteLn('signed, but not a DocMDP certification');
      end;
    end;
end;

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

بقیه سطح: فایل‌های جاسازی شده و XFA

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

حضور XFA پرچم دیگر است. یک فرم XFA فرم ایستا (AcroForm) را با یک معماری فرم مبتنی بر XML جایگزین می‌کند که رندر و مدل اسکریپت‌نویسی خاص خود را به همراه دارد، سطحی بزرگ‌تر و پیچیده‌تر از یک فرم ساده. برای توجه به وجود آن نیازی به پردازش XFA ندارید؛ صرف وجود آن نشانه‌ای است که فایل دارای یک لایه تعاملی غنی‌تر است که ارزش بررسی دقیق‌تر را دارد. کامپوننت آن را به عنوان یک منطق (boolean) واحد گزارش می‌کند

var
  I: Integer;
begin
  if Pdf.XFA then
    WriteLn('NOTE: document contains an XFA form layer');

  if Pdf.AttachmentCount > 0 then
  begin
    WriteLn('embedded files: ', Pdf.AttachmentCount);
    for I := 0 to Pdf.AttachmentCount - 1 do
      WriteLn('  - ', Pdf.AttachmentName[I]);
  end;
end;

یک روال فقط‌خواندنی که یک گزارش می‌نویسد

قطعات را کنار هم قرار دهید و حسابرسی یک رویه واحد است که یک سند را بارگذاری می‌کند، اسکریپت‌ها و بدنه آن‌ها را برمی‌شمارد، اهداف Launch و URI آن را فهرست می‌کند، سطح مجوز MDP امضا را گزارش می‌کند، پیوست‌ها و XFA را یادداشت می‌کند، و یافته‌ها را در یک گزارش می‌نویسد. این هیچ چیزی را رندر نمی‌کند، بنابراین ارزان است و نمی‌تواند برای نمایش محتوای متخاصم صفحه فریب داده شود. خروجی یک رکورد مسطح (flat) و قابل خواندن برای انسان است که یک بازرس یا یک قانون در پایین‌دست می‌تواند بر اساس آن عمل کند

شکلی که در عمل به خوبی کار می‌کند این است که هر یافته را به عنوان یک خط جمع‌آوری کنید، موارد واقعاً خطرناک را پیشوند کنید تا در بالای صف بررسی مرتب شوند، و کل آن را در کنار فایل ذخیره کنید. سندی که هیچ اسکریپتی ندارد، هیچ اقدام راه‌اندازی (launch) ندارد، هیچ پیوستی ندارد، هیچ XFA ندارد، و یا امضایی ندارد یا یک گواهی‌نامه منسجم دارد، به آرامی عبور می‌کند. سندی که چندین پرچم را همزمان فعال می‌کند، سندی است که یک شخص باید قبل از هر مرحله بعدی که آن را باز می‌کند، ببیند. حسابرسی تصمیم اعتماد را برای شما نمی‌گیرد. فقط مطمئن می‌شود که تصمیم آگاهانه است تا کورکورانه

هنگامی که یک فایل از حسابرسی عبور می‌کند و شما واقعاً نیاز به دیدن آن دارید، این کار را تحت محدودیت انجام دهید تا در یک نمایشگر پیش‌فرض. رویکردِ راهنمای ما در ساخت یک پیش‌نمایش ایمن PDF در دلفی نشان می‌دهد که چگونه هندلینگ خودکار لینک و محتوای فعال را از عملکرد در حین نگاه کنترل شده بازداریم. برای گنجاندن این شمارش در یک خط لوله کاملِ دریافت با ابزار بازرس، مقاله میز کار دریافت و بررسی PDF را ببینید. هر دو روی همان پایه فقط‌خواندنی و بدون رندر ساخته شده‌اند و به عنوان بخشی از کامپوننت PDFium برای دلفی و C++Builder، در کنار API‌های رندر، متن، فرم، و امضا که در جای دیگر این وبلاگ پوشش داده شده‌اند، ارسال می‌شوند