تطهير ملفات PDF غير الموثوقة: نزع المحتوى وإعادة بنائه
Spec: ISO 32000-2, §12.6.4ISO 32000-2 §12.6.4
لمحة سريعة
قسم بعنوان «لمحة سريعة»ملف PDF يصل من العالم الخارجي شيفرة بقدر ما هو مستند. يمكنه حمل JavaScript، وإجراء Launch، وملفاً تنفيذياً مُضمَّناً، وبنيةً مصنوعةً لإسقاط محلِّل. يعامل نزع المحتوى وإعادة بنائه (CDR) ذلك الملف غير موثوق به، ويُبقي فقط ما هو آمن، ويعيد بناء ملف PDF نظيف من الناجين.
تشرح هذه الصفحة كيف يفعل CdrEngine في NextPDF Enterprise ذلك — والشيء الصادق الواحد الذي ينبغي لكل مزوّد CDR قوله بصوت عالٍ: الملف المُعاد بناؤه إسقاط أمني للأصل، لا نسخة وفية له.
لماذا يهم هذا
قسم بعنوان «لماذا يهم هذا»الأجزاء الخطيرة من ملف PDF ليست غريبة. إنها مزايا قياسية. يعرّف المعيار فهرساً كاملاً من الإجراءات — ما يحدث حين يُفتَح مستند، وحين تُعرَض صفحة، وحين يتغير حقل — وذلك الفهرس يشمل إجراءات JavaScript وLaunch (Spec: ISO 32000-2, §12.6.4ISO 32000-2 §12.6.4). العارض الذي يحترم التنسيق سيشغّلها بسرور. تلك هي موطئ قدم المهاجم: ملف صحيح تماماً وخبيث تماماً في آنٍ واحد.
الترشيح بحسب امتداد الملف لا يفعل شيئاً هنا. التهديد داخل ملف PDF حسن التكوين، فالدفاع الحقيقي الوحيد هو فتحه وفهمه وتجريد الآلية النشطة قبل أن تبلغ مُصيِّراً أبداً. هذا هو موقف التحقق من المُدخَلات الذي يصفه إرشاد OWASP لرفع الملفات والتحقق من المُدخَلات: لا تثق بالبايتات أبداً، وفضّل إعادة بناء أثرٍ معروف الصلاح على فحص أثرٍ عدائي بحثاً عن توقيعات معروفة السوء.
الخلاصة المختصرة
قسم بعنوان «الخلاصة المختصرة»- يفترض CDR أن المُدخَل عدائي ويُنتِج ملفاً جديداً بدلاً من ترقيع القديم.
- يشغّل
CdrEngine::sanitize()ست مراحل: تحليل، وتحكّم في القبول، واكتشاف تهديدات، وترشيح، وتنظيف مراجع، وإعادة بناء. - يعيد
CdrResultيخبرك بما أُزيل، وما إذا كان المستند قد قُبِل أصلاً، و — إن لم يُقبَل — لماذا رُفِض. - المُخرَج إسقاط أمني. وهو صراحةً ليس نسخة إثباتية، ولا تطابق تجزئة، ولا أثرَ أرشفة. ذلك عقد، لا تحفّظ.
- هذا لـEnterprise فقط. نواة NextPDF لا تؤدي CDR.
كيف يتعامل NextPDF مع هذا
قسم بعنوان «كيف يتعامل NextPDF مع هذا»تذكر وثائق المحرك نفسه الخط الأحمر للتصميم في ثلاث كلمات: طبقة الإسقاط الأمني. sanitize() تحويل مُتلِف غير عكوس. مسموح له بطرح بايتات بعيداً. ما لا يُسمَح له به هو التظاهر بأن النتيجة هي المستند نفسه.
خط الأنابيب مرتَّب عمداً. تضيّق كل مرحلة الثقة قبل أن تعمل المرحلة التالية عليها.
- ParsePdfReader builds the object graph from the raw bytes. A parse failure is a rejection, not a best-effort guess.
- Admission controlResource limits are checked first — object count, page count, decoded-stream size, per-stream inflation ratio. Over-budget input is rejected, never sanitised.
- DetectThreatDetector walks the graph and records each dangerous feature as a DetectedThreat with its object number and type.
- FilterObjects are partitioned into safe and removed. Catalog-level keys are stripped in place so the document catalog itself survives.
- Reference scrubPointers to removed objects are cleaned so the rebuilt file has no dangling references.
- RebuildCdrRebuilder serialises only the safe objects into a new PDF. The output is fresh structure, not an edited original.
القبول قبل النزع. قبل إزالة تهديد واحد، يسأل المحرك ما إذا كان المستند يستحق المعالجة أصلاً. يحمل CdrPolicy الحدود — maxObjects، وmaxPageCount، وmaxDecodedStreamBytes، وmaxInflationRatio تدافع عن قنابل فك الضغط. المستند الذي يتجاوزها يُرفَض، ويقول CdrResult ذلك بـadmitted: false وrejectionReason. هذا التمييز حامل: “نظّفناه” و”رفضناه” نتيجتان مختلفتان، ويفصلهما نوع النتيجة كي يفصلهما تقرير أخطائك أيضاً.
التهديدات مُسمّاة، لا مُخمَّنة. يربط ThreatDetector::detect() كل بنية خطيرة بـThreatType — JavaScript، وLaunchAction، وOpenAction، وAdditionalActions، وRemoteGoTo، وSubmitForm، وImportData، وEmbeddedFiles، وRichMedia، وNamedJavaScript، وأكثر — كلٌّ مربوط بميزة PDF محددة. الكائن الذي يتعذّر تحليله إطلاقاً يصير نوع تهديده الخاص، UnparseableObject، لأن الكائن الذي يتعذّر على المُطهِّر قراءته كائن يتعذّر عليه ضمانه. كل اكتشاف DetectedThreat يحمل رقم الكائن المخالف، فتكون الإزالة دقيقة.
الإزالة تحمي المستند، لا الحمولة وحدها. بعض المفاتيح الخطيرة تستقر على فهرس المستند (الجذر) — /OpenAction ينطلق عند الفتح مثلاً. إزالة كائن الجذر بأكمله لقتل مفتاح واحد ستدمّر الفهرس وتعطّل الملف بصمت. يتولّى المحرك هذه تجريد مفاتيح في مكانها على الفهرس بدلاً من ذلك، وكحارس يفشل مغلقاً يرفض إصدار مستند مُعاد بناؤه فقد /Root خاصته أصلاً. المُطهِّر الذي يُنتِج ملفاً معطوب البنية بينما يبلّغ عن نجاح هو بالضبط نمط الإخفاق الذي وُجِد هذا الحارس لمنعه.
مثال عملي
قسم بعنوان «مثال عملي»الشكل أدناه هو نقطة الدخول الحقيقية. تسلّم المحرك بايتات خاماً وسياسة؛ فتستعيد CdrResult صادقاً بشأن ما حدث.
<?php
declare(strict_types=1);
use NextPDF\Enterprise\Security\Cdr\CdrEngine;use NextPDF\Enterprise\Security\Cdr\CdrPolicy;
$engine = new CdrEngine();
// Standard policy removes the known active threats — JavaScript, Launch// actions, remote go-to, form submit/import, and the rest — while leaving// the lossy opt-in "Strip*" cases off by default.$result = $engine->sanitize($untrustedPdfBytes, CdrPolicy::standard());
if (!$result->admitted) { // Rejected by admission control (e.g. object/page limit, zip bomb). // This is NOT a sanitised document. Do not serve it; report the reason. throw new \RuntimeException($result->rejectionReason);}
if ($result->hadThreats()) { // The disarmed bytes are safe to render. Each removed threat carries its // type and object number for your audit log — never silently. foreach ($result->removedThreats as $threat) { error_log(\sprintf( 'CDR removed %s in object %d', $threat->type->value, $threat->objectNumber, )); }}
$cleanBytes = $result->sanitizedPdf; // The security projection. Not the original.لا يوجد مسار تسلّم فيه هذه الشيفرة ملفاً لا يزال خطيراً بهدوء. فإما أن يُقبَل المستند ويُنزَع سلاحه، أو يُرفَض بسبب مذكور. قائمة removedThreats تعني أن النزع قابل للتدقيق، لا سحري.
مفهوم خاطئ شائع
قسم بعنوان «مفهوم خاطئ شائع»“CDR مجرد تنقيح بخطوات إضافية.”
ليس كذلك، والخلط بينهما خطير. يزيل التنقيح المعلومات — الأسماء، وأرقام الحسابات، والمحتوى الذي يجب ألا يراه إنسان. ويزيل CDR القدرة — الـJavaScript، وإجراء Launch، والحمولة المُضمَّنة التي يجب ألا تشغّلها آلة. لهما معايير نجاح متعاكسة. التنقيح صحيح حين يزول المحتوى الحساس ويُحفَظ الباقي حرفياً. والنزع صحيح حين يزول التهديد، وهو مستعد تماماً لتغيير بنية حميدة للوصول إلى ذلك. استخدم الأداة التي تطابق قصدك؛ لا تلجأ إلى إحداهما متوقعاً ضمانات الأخرى.
مفهوم خاطئ ثانٍ هو أن إعادة بناء نظيفة تثبت أن الأصل كان نظيفاً. إنها لا تثبت شيئاً عن الأصل. تثبت فقط أن المُخرَج لا يحوي تهديداً مكتشَفاً. ربما كان المُدخَل سلاحاً؛ ومهمة CDR أن تضمن أن ما تمرّره في مرحلة لاحقة ليس كذلك.
الحدود والقيود
قسم بعنوان «الحدود والقيود»هذا هو الجزء الذي تتخطّاه الكتيّبات الدعائية، فسنقوله صراحةً.
- المُخرَج إسقاط أمني، لا نسخة إثباتية. يحمل مصدر
CdrEngineهذا خطاً أحمر معمارياً. ملف PDF المُطهَّر يجب ألا يُستخدَم لحفظ أدلة قانونية، ولا لمقارنة تجزئة مع الأصل، ولا نسخة أرشفة. التحويل مُتلِف وغير عكوس بالتصميم. - للاكتشاف نطاق. يزيل CDR التهديدات التي يعرف كيف يسمّيها. إنه طبقة قوية قابلة للتدقيق في حِزمة دفاع-في-عمق — لا ضمان بأن ملفاً خالٍ من كل تقنية مستقبلية ممكنة. أبقِه خلف التحقق من الرفع نفسه، وفحوص نوع المحتوى، ومعالجة الامتياز الأدنى التي يصفها إرشاد OWASP لرفع الملفات.
- بعض السياسات مُتلِفة عمداً. حالات
Strip*الاختيارية تزيل الملفات المُضمَّنة، والتوقيعات، وحقول النماذج، والطبقات، والوسائط ثلاثية الأبعاد. تلك قوية وستحذف محتوى مشروعاً — حمولة فاتورة ZUGFeRD/Factur-X مثلاً. وهي مُطفأة افتراضياً لذلك السبب بالضبط. شغّلها عن علم. - التوقيعات لا تنجو من إعادة البناء. إعادة بناء الملف تغيّر بايتاته، فأي توقيع رقمي أصلي لا يعود يطابق نطاق بايتاته. المستند المنزوع سلاحه غير موقَّع بالنسبة للمصدر. إن احتجت إلى أثرٍ موقَّع، فوقّع المُخرَج النظيف عملاً جديداً.
| Edition | Availability |
|---|---|
| Core | غير متاح. نواة NextPDF لا تؤدي CDR. تحلّل ملفات PDF وتعرضها وتكتبها؛ لا تكتشف التهديدات ولا تعيد بناء مُدخَل غير موثوق. |
| Pro | غير متاح في إصدار Pro. |
| Enterprise | متاح عبر |
مستندات ذات صلة
قسم بعنوان «مستندات ذات صلة»- كيف يعمل تشفير PDF فعلاً — النصف الآخر من التعامل مع ملفات PDF الحساسة: حماية المحتوى مقابل إزالة القدرة.
- الأخطاء بوصفها ميزة — فلسفة الفشل-مغلقاً التي تجسّدها رفوض التحكم في القبول وحارس
/Rootفي CDR. - واجهة برمجية ترفض التخمين — لماذا يتفوّق نوع نتيجة يميّز المُنظَّف عن المرفوض على بذل جهد صامت.
مسرد المصطلحات
قسم بعنوان «مسرد المصطلحات»- CDR (نزع المحتوى وإعادة بنائه Content Disarm & Reconstruction) — استراتيجية تطهير تحلّل ملفاً غير موثوق، وتزيل المكوّنات النشطة أو الخطيرة، وتعيد بناء ملف نظيف من الباقي الآمن، بدلاً من الفحص بحثاً عن توقيعات معروفة السوء.
- الإسقاط الأمني — مُخرَج مُطهَّر يحفظ قدراً كافياً من المصدر ليكون مفيداً مع ضمان إزالة التهديدات. وهو عمداً ليس وفياً للبايتات وغير صالح للأدلة أو التجزئة أو الأرشفة.
- التحكم في القبول — البوابة السابقة للتطهير التي ترفض المستندات المتجاوزة لحدود الموارد (عدد الكائنات، عدد الصفحات، حجم الدفق المفكوك، نسبة الانتفاخ) قبل بدء أي عمل نزع.
- الإجراء — بنية PDF تجعل شيئاً يحدث على مُحفِّز كفتح المستند أو تغيّر حقل؛ فهرس أنواع الإجراءات (Spec: ISO 32000-2, §12.6.4ISO 32000-2 §12.6.4) يشمل إجراءات JavaScript وLaunch، سطح تهديد CDR النموذجي.
- المرجع المعلَّق — مؤشر إلى كائن لم يعد موجوداً بعد الترشيح. تزيل مرحلة تنظيف-المراجع هذه كي يبقى الملف المُعاد بناؤه متسقاً بنيوياً.