مقاله فنی

رمزنگاری AES-256 فایل‌های PDF در دلفی: راه‌اندازی HotPDF و دام‌های آن

پرچم دسترسی (permission flag) در PDF یک قفل نیست. این پرچم درخواستی است که فایل از برنامه‌ای که آن را باز می‌کند دارد، و نمایش‌دهنده می‌تواند آن را نادیده بگیرد. این واقعیت ساده تعیین می‌کند که چگونه باید درباره سایر انتخاب‌های موجود در این صفحه استدلال کنید. محرمانگی واقعی تنها از یک جا ناشی می‌شود: رمزنگاری AES-256 که بر اساس رمز عبوری کلیدگذاری شده است که خواننده آن را ندارد. هر چیز دیگری، نظیر کادرهای تایید "عدم چاپ" و "عدم کپی"، سیاستی است که نرم‌افزارهای سازگار با آن موافقت می‌کنند و نرم‌افزارهای مخرب به آن پایبند نیستند. اگر این دو لایه را با هم اشتباه بگیرید، محصولی ارائه می‌دهید که در نسخه نمایشی امن به نظر می‌رسد اما در محیط واقعی نشت اطلاعات دارد

HotPDF یک کامپوننت بومی VCL برای PDF در دلفی و C++Builder است و مدل حفاظتی ISO 32000 را از طریق مجموعه‌ای کوچک از ویژگی‌ها در دسترس قرار می‌دهد. تنظیم این ویژگی‌ها آسان است. بخش دشوار این است که بدانید کدام‌یک از آن‌ها حفاظت رمزنگاری برای شما فراهم می‌کند و کدام‌یک فقط یک پیشنهاد مودبانه است، و همچنین رعایت ترتیب صحیح مقداردهی تا رمزنگاری درخواستی شما به درستی اعمال شود

دو رمز عبور واقعاً چه قولی می‌دهند

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

بیت‌های دسترسی پایه و اساس ضعیف‌تری دارند. چاپ، استخراج محتوا، پر کردن فرم: هر یک از این‌ها پرچمی است که نمایش‌دهنده می‌خواند و تصمیم می‌گیرد که به آن احترام بگذارد (ISO 32000-2 §7.6.4). رمزنگاری از بایت‌ها محافظت می‌کند. پرچم‌های دسترسی فقط به نرم‌افزارهای سازگار دستور می‌دهند، و این دستور را پس از رویداد صادر می‌کنند. هر کسی که سند را با رمز عبور کاربر باز کند، در حال حاضر محتوای رمزگشایی شده را در حافظه دارد، بنابراین "بدون کپی" و "بدون چاپ" برای یک نمایش‌دهنده خوش‌رفتار معنی دارد اما برای یک برنامه‌ی مصمم بی‌معنی است. مدل تهدید خود را حول این مرز بنا کنید. محرمانگی در رمز عبور کاربر نهفته است. مجوزها تنها ویژگی‌هایی را شکل می‌دهند که نمایش‌دهنده‌های رایج ارائه می‌دهند، و این تمام کاری است که انجام می‌دهند

ترتیب پیکربندی: همه چیز پیش از BeginDoc

HotPDF دیکشنری رمزنگاری را ایجاد کرده و کلید فایل را در لحظه‌ای که BeginDoc اجرا می‌شود استخراج می‌کند. هر آنچه که ویژگی‌های حفاظتی در آن لحظه دارند، همان چیزی است که سند دریافت می‌کند و تغییر آن‌ها پس از آن هیچ اثری ندارد. مهم‌ترین ویژگی در اینجا CryptKeyLength است که طرح رمزنگاری را از مقادیر THPDFKeyType یعنی k40، k128، aes128 و aes256 انتخاب می‌کند. اگر آن را بعد از BeginDoc تنظیم کنید، هیچ استثنایی (exception) و هشداری دریافت نمی‌کنید، بلکه فایلی تولید می‌شود که به آرامی با همان تنظیمات اولیه‌اش باقی می‌ماند. این نوع انحراف پنهان بدترین نوع است: تمام تست‌های محلی را پشت سر می‌گذارد و ماه‌ها بعد به عنوان یک مشکل انطباق در سیستم مشتری نمایان می‌شود

var
  Pdf: THotPDF;
begin
  Pdf := THotPDF.Create(nil);
  try
    Pdf.FileName := 'statement.pdf';
    Pdf.ActivateProtection := True;
    Pdf.CryptKeyLength := aes256;        // must be set before BeginDoc
    Pdf.UserPassword := 'open-secret';
    Pdf.OwnerPassword := 'admin-secret';
    Pdf.UseAES256R6 := False;            // R=5: widest viewer support
    Pdf.BeginDoc;
    Pdf.CurrentPage.SetFont('Arial', [], 11);
    Pdf.CurrentPage.TextOut(50, 720, 0, 'Account statement, June 2026');
    Pdf.EndDoc;
  finally
    Pdf.Free;
  end;
end;

رمزهای عبور دارای کدگذاری UTF-8 هستند و به 127 بایت محدود شده‌اند، که حد تعیین شده در ISO 32000-2 برای طرح‌های AES-256 است. اگر سیاست رمز عبور شما کلیدهای طولانی‌تری در اختیارتان می‌گذارد، کوتاه‌سازی را خودتان در سمت برنامه خودتان انجام دهید تا کنترل دقیقی بر نقطه برش داشته باشید. اگر این کار را به شانس و کتابخانه بسپارید، ممکن است یک نمایش‌دهنده در آینده با نقطه برش موافق نباشد و در نتیجه فایلی تولید شود که برای شما باز می‌شود اما همان رمز عبور را در جای دیگر نمی‌پذیرد

نسخه ۵ یا نسخه ۶: یک متغیر بولی، دو اکوسیستم

ویژگی UseAES256R6 بین دو پروتکل (handshake) AES-256 انتخاب می‌کند و این انتخاب بسیار مهم‌تر از آن است که نوع بولی آن نشان می‌دهد. اگر آن را روی False رها کنید، HotPDF نسخه ۵ را می‌نویسد، طرح AES-256 که به عنوان افزونه‌ای به PDF 1.7 اضافه شد و نمایش‌دهنده‌های پانزده سال اخیر می‌توانند آن را باز کنند. اگر آن را True تنظیم کنید، نسخه ۶ را دریافت می‌کنید، روش استخراج کلید سخت‌گیرانه‌تری که در ISO 32000-2 برای PDF 2.0 استاندارد شده است و ضعف شناخته‌شده‌ای در نحوه بررسی رمز عبور توسط نسخه ۵ را برطرف می‌کند

بنابراین از نظر رمزنگاری، نسخه ۶ داستان بهتری دارد. اما در عین حال همان چیزی است که باعث خرابی می‌شود. یک فایل نسخه ۶ به نمایش‌دهنده‌ای نیاز دارد که برای PDF 1.7 سطح افزونه ۳ یا PDF 2.0 ساخته شده باشد، و بسیاری از نرم‌افزارهای مورد استفاده در عمل هیچکدام از این دو نیستند: بایگانی‌های مدیریت سوابق، رندرهای تعبیه‌شده در محصولات دیگر، ابزارهای تجاری که سال‌ها کسی به آن‌ها دست نزده است. این برنامه‌ها فایل را کاملاً رد خواهند کرد و این اتفاق در ماشین مشتری رخ خواهد داد، نه در ماشین شما. بنابراین، پیش‌فرض عملی همان نسخه ۵ است. به نسخه ۶ فقط زمانی رجوع کنید که یک سیاست امنیتی مشخصاً نسخه ISO 32000-2 را الزامی کند، و زمانی که واقعاً تأیید کرده‌اید که تمام مصرف‌کنندگان قادر به خواندن آن هستند. در هر صورت، یادداشت کنید که کدام یک را انتخاب کرده‌اید و چرا، زیرا نفر بعدی که این کد را می‌خواند متعجب خواهد شد

انواع کلیدهای قدیمی‌تر مستحق یک جمله هستند تا بدانید که باید از آن‌ها اجتناب کنید. THPDFKeyType هنوز هم k40، k128 و aes128 را فهرست می‌کند، اما آن‌ها برای بازتولید بایگانی‌های تاریخی وجود دارند، نه برای محافظت از بایگانی‌های جدید. RC4 40-بیتی به سادگی توسط سخت‌افزارهای معمولی شکسته می‌شود و طرح‌های 128-بیتی پیش از نسخه‌های AES-256 معرفی شده‌اند که هر بررسی امنیتی فعلی انتظار آن را دارد. برای سندی که در سال 2026 ایجاد می‌کنید، سؤال واقعی تنها تقابل نسخه ۵ و ۶ است؛ اگر در یک طراحی جدید به سراغ انواع کلید قدیمی می‌روید، حتماً مشکلی اساسی در زنجیره پردازشی شما وجود دارد

پرچم‌های دسترسی بدون رمز عبور برای باز کردن

اغلب نیازمندی‌ها برعکس محرمانگی است. همه باید بتوانند سند را بخوانند، اما چاپ یا استخراج محتوا باید محدود باشد. شما می‌توانید این حالت را با یک رمز عبور کاربر خالی و یک رمز عبور مالک غیرخالی بیان کنید، که در PDF حالت رمز عبور باز نامیده می‌شود، و عملیاتی را که می‌خواهید اجازه دهید در ProtectOptions لیست می‌کنید

Pdf.ActivateProtection := True;
Pdf.CryptKeyLength := aes256;
Pdf.UserPassword := '';                      // anyone can open the file
Pdf.OwnerPassword := 'rotate-me-quarterly';  // guards the permission set
Pdf.ProtectOptions := [prPrint, prPrint12bit, prExtractContent];
Pdf.BeginDoc;
// ... page content ...
Pdf.EndDoc;

مجموعه THPDFProtectOptions با بیت‌های مجوز ISO نقشه‌برداری می‌شود: prPrint و prPrint12bit برای چاپ با وضوح بالا، prInformationCopy برای کپی و استخراج عمومی، prExtractContent برای استخراج فناوری‌های کمکی، به علاوه prModifyStructure، prEditAnnotations، prFillAnnotations و prAssemble. دو مورد از آن‌ها نیاز به هشدار دارند. prExtractContent را تقریباً در هر نمایه‌ای که می‌سازید روشن بگذارید. این بیتی است که یک صفحه‌خوان برای دسترسی به متن نیاز دارد، و پاک کردن آن به طور خاموش یک تصمیم حقوقی را به یک نقص دسترسی‌پذیری تبدیل می‌کند که افراد دارای معلولیت با آن برخورد می‌کنند و شما هرگز آن را نمی‌بینید. تله دیگر استفاده از prPrint به تنهایی، بدون prPrint12bit است: چندین نمایش‌دهنده به این حالت با کاهش کیفیت چاپ پاسخ می‌دهند، و کاربران شما این را به عنوان یک باگ رندر کردن گزارش خواهند داد در حالی که در واقعیت به دلیل تنظیمات دسترسی است

اعتبارسنجی فقط پنج دقیقه زمان می‌برد و باید بخشی از چک‌لیست انتشار شما باشد. یک نمونه از هر نمایه را در آکروبات باز کنید، ویژگی‌های سند را باز کنید و برگه امنیت را بخوانید، که الگوریتم را مشخص می‌کند ("AES 256-bit") و عملیات مجاز را یکی یکی فهرست می‌کند. سپس همان فایل را در قدیمی‌ترین نمایش‌دهنده‌ای که مشتریان شما در واقع استفاده می‌کنند باز کنید، نه جدیدترین نسخه روی دستگاه خودتان. این بررسی دوم، بیمه‌ای ارزان است تا از عبور فایل نسخه ۶ از مرحله توسعه و از کار افتادن آن نزد مشتری که هرگز سیستم خود را ارتقا نداده است، جلوگیری کند

حذف محافظت از فایل‌های موجود

رمزگشایی، همان مدل ویژگی را به صورت معکوس اجرا می‌کند. سند را با یک اعتبارنامه معتبر بارگیری کنید، محافظت را خاموش کنید و نتیجه را بدون آن ذخیره کنید

var
  Pdf: THotPDF;
  PageCount: Integer;
begin
  Pdf := THotPDF.Create(nil);
  try
    PageCount := Pdf.LoadFromFile('encrypted.pdf', 'open-secret');
    if PageCount > 0 then
    begin
      Pdf.ActivateProtection := False;   // drop encryption on save
      Pdf.SaveLoadedDocument('plain.pdf');
    end;
  finally
    Pdf.Free;
  end;
end;

این مسیر، کل سند را در حافظه پردازش می‌کند، که برای فایل‌های معمولی مناسب است اما برای فایل‌های بسیار بزرگ اتلاف حافظه در پی دارد. وقتی حجم ورودی به صدها مگابایت می‌رسد، DecryptFile گزینه ارزان‌تری است: این روش فایل را در طول کپی در سطح فایل رمزگشایی می‌کند و مسیر بازنویسی مستقیم AES-256 را در پیش می‌گیرد که در صورت امکان از ساختن درخت اشیاء کامل صرف نظر می‌کند. این بخشی از Direct File API است که در مقاله مربوط به پردازش PDF‌های بزرگ از دلفی به آن پرداخته شده است

محدودیت‌هایی که با رمزنگاری در تعامل هستند

قبل از اینکه بر اساس رمزنگاری طراحی کنید، دانستن دو محدودیت ارزش دارد. اولین مورد انطباق بایگانی است. ISO 19005 رمزنگاری را در PDF/A ممنوع می‌کند، بنابراین هر جریان کاری که یک سند را رمزنگاری می‌کند و در عین حال ادعای انطباق PDF/A دارد، از نظر ساختاری متناقض است؛ HotPDF به شما اجازه نمی‌دهد هر دو را در یک فایل داشته باشید. در مواقعی که واقعاً به هر دو نیاز دارید، راه‌حل ساختن دو نسخه است: یک نسخه رمزگذاری‌شده برای توزیع و یک نسخه رمزنگاری‌نشده جداگانه برای بایگانی

محدودیت دوم سخت‌تر است. رمزنگاری PDF هیچ سیستم امانی (escrow) و هیچ راهی برای بازیابی ندارد. اگر رمز عبور کاربر در فایل R5 یا R6 گم شود، تنها گزینه‌های شما حملات بروت‌فورس (brute force) یا دست کشیدن از تلاش است. بنابراین، با رمزهای مالک و کاربر همانند هر اعتبارنامه مهم سیستم برخورد کنید. آن‌ها را تولید کنید، در یک صندوقچه امن (vault) ذخیره کنید و بر اساس برنامه زمانی آن‌ها را تغییر دهید. تنها کاری که هرگز نباید انجام دهید این است که آن‌ها را به عنوان ثابت‌های برنامه‌نویسی به شکل هاردکد در یک یونیت قرار دهید، جایی که به طور مستقیم وارد کنترل نسخه می‌شوند و برای همیشه در سیستم‌های تمام توسعه‌دهندگان باقی می‌مانند

یک عادت نهایی که ارزش ایجاد دارد. تغییر محافظت فایلی که شما ایجاد نکرده‌اید، دقیقاً همان مکانیزم رمزگشایی است، نه یک ویژگی جداگانه: آن را با رمز عبورش از طریق LoadFromFile بارگیری کنید، ProtectOptions یا رمزهای عبور را در محل تغییر دهید، و با استفاده از SaveLoadedDocument آن را دوباره ذخیره کنید. اگر بتوانید فایلی را رمزگشایی کنید، می‌توانید مجوزهای آن را نیز تغییر دهید، و کد آن تقریباً شبیه مثال بالا به نظر می‌رسد

ویژگی‌های حفاظتی نشان داده شده در اینجا بخشی از استاندارد کامپوننت HotPDF برای دلفی و C++Builder است؛ صفحه این محصول شامل مرجع کامل رمزنگاری از جمله لیست کامل پرچم‌های دسترسی است