تشفير ملف PDF وتقييد الأذونات
لمحة سريعة
قسم بعنوان «لمحة سريعة»تشرح هذه الوصفة تشفير مستند باستخدام معالج الأمان القياسي Advanced Encryption Standard (AES)-256. حدّد كلمة مرور للمستخدم (مطلوبة للفتح)، وكلمة مرور للمالك (وصول كامل)، وقناع بتات للأذونات يقيّد العمليات. تعامل مع تلك الأذونات بوصفها معتمِدة على تعاون القارئ: فالتشفير يمنح السرية لا السلامة، ولا تحترم بتات الأذونات إلا البرمجيات المتعاونة. تتبع الوصفة examples/22-protection.php.
حد الثقة (احمل هذا مع كل ادعاء متعلق بالأذونات). تشفير PDF يحمي سرية المحتوى من الأطراف التي لا تملك كلمة المرور (ISO 32000-2 §7.6). وهو لا يحمي السلامة: فهو لا يكتشف ولا يمنع التعديل. مُدخَل الأذونات
Pهو مجموعة أعلام بطول 32 بت غير مُوقَّعة تطلب من القارئات المطابِقة احترام القيود؛ وهي ليست تحكمًا في الوصول. أي أداة غير مطابِقة، أو أي أداة تُستخدم مع كلمة مرور المالك، يمكنها تنفيذ كل عملية “مرفوضة”. لا تصِف ملف PDF مشفَّرًا بأنه “آمن”، أو “مقاوم للعبث”، أو “محمي من النسخ”.
التثبيت
قسم بعنوان «التثبيت»composer require nextpdf/core:^3فعّل امتداد PHP المسمّى openssl. يستخدمه مُشفّر AES-256 في التشفير واشتقاق المفتاح.
نظرة مفاهيمية عامة
قسم بعنوان «نظرة مفاهيمية عامة»يحدّد رمزا قاموس التشفير V/R معالج الأمان القياسي (ISO 32000-2 §7.6). يُنفّذ Aes256Encryptor الخاص بـNextPDF مرشّح التشفير AESV3 عند المراجعة 6 لمعالج الأمان (V=5/R=6). يستخدم مفتاح تشفير ملف عشوائيًّا بطول 256 بت، واشتقاق مفتاح بتجزئة تكرارية مُملَّحة (الخوارزمية 2.B)، وتشفيرًا لكل كائن بنمط AES-256-CBC مع متجه تهيئة عشوائي. يُعد تسلسل كتل الشيفرة Cipher Block Chaining (CBC) نمط سرية (NIST SP 800-38A)، ويجب أن تكون متجهات التهيئة الخاصة به غير قابلة للتنبؤ.
متجه التهيئة جديد لكل كائن ولكل تشغيل، لذلك تختلف البايتات الخام من تشغيل إلى آخر. لذلك يكون ملف تعريف قابلية إعادة الإنتاج هو structural. قبل أن تقارن مجموعة الاختبار بين تشغيلين، تُطبّع متجه تهيئة التشفير، وترتيب الكائنات، و/ID الخاص بالمذيّل. هذا الملف أكثر صرامة من ملف وصفة لا تستخدم التشفير.
يُضبط قناع بتات الأذونات في المُدخَل P. يمنح البت 3 الطباعة، ويمنح البت 6 annotation/form-fill. والقيمة هي الكمية الموثَّقة بطول 32 بت غير مُوقَّع.
سطح واجهة برمجة التطبيقات API
قسم بعنوان «سطح واجهة برمجة التطبيقات API»NextPDF\Core\Concerns\HasSecurity (مدمَج في Document):
setEncryption(#[SensitiveParameter] string $userPassword, #[SensitiveParameter] string $ownerPassword = '', int $permissions = -1): static— يُهيّئ تشفير معالج الأمان القياسي AES-256. يمنحpermissions = -1جميع الأذونات. عندما تكونownerPasswordفارغة، تُعاد كلمة مرور المستخدم استخدامًا ككلمة مرور للمالك. استدعِها قبلaddPage().getEncryptor(): ?Aes256Encryptor— المُشفّر المُهيَّأ، أوnull.useAesGcm(?bool $enabled = true): static— يفعّل الاشتراك في ISO/TS 32003 AES-256-GCM؛ ويرمي استثناءً إذا لم يوفّر OpenSSL/libsodium المضيف البدائيات التشفيرية.
كلتا معلَمتي كلمتي المرور موسومتان بـ#[SensitiveParameter]، لذا تحجبهما PHP من تتبّعات المكدّس.
بتات الأذونات (المُدخَل P، البتات الدنيا 3–6 الشائعة الاستخدام):
| البت | القيمة | العملية |
|---|---|---|
| 3 | 4 | طباعة المستند |
| 4 | 8 | تعديل محتويات المستند |
| 5 | 16 | نسخ / استخراج النص والرسوم |
| 6 | 32 | إضافة التعليقات التوضيحية أو تعديلها وتعبئة حقول النماذج |
نموذج شيفرة — بداية سريعة
قسم بعنوان «نموذج شيفرة — بداية سريعة»<?php
declare(strict_types=1);
require_once __DIR__ . '/vendor/autoload.php';
use NextPDF\Core\Document;
$doc = Document::createStandalone();$doc->setTitle('Confidential Memo');
// Grant printing only (bit 3 = 4). MUST run before addPage().$doc->setEncryption( userPassword: 'open-me', ownerPassword: 'owner-secret', permissions: 4,);
$doc->addPage();$doc->setFont('helvetica', '', 12);$doc->cell(0, 10, 'Encrypted with AES-256; printing allowed only.', newLine: true);
$doc->save(__DIR__ . '/confidential.pdf');echo "Wrote confidential.pdf\n";نموذج شيفرة — بيئة الإنتاج
قسم بعنوان «نموذج شيفرة — بيئة الإنتاج»يعكس المثال الكامل أدناه examples/22-protection.php ويكتب إلى NEXTPDF_COOKBOOK_OUTPUT من أجل مجموعة الاختبار.
<?php
declare(strict_types=1);
require_once __DIR__ . '/vendor/autoload.php';
use NextPDF\Core\Document;
$userPassword = 'demo';$ownerPassword = 'admin';
// Grant ONLY printing (bit 3 = 4); deny copy/modify/annotate.$permissions = 4;
$doc = Document::createStandalone();$doc->setTitle('Encrypted Document — Restricted Permissions');$doc->setAuthor('NextPDF Example');
// setEncryption() MUST be called before addPage().$doc->setEncryption( userPassword: $userPassword, ownerPassword: $ownerPassword, permissions: $permissions,);
$doc->addPage();$doc->setFont('helvetica', 'B', 20);$doc->cell(0, 14, 'Encrypted PDF Document', newLine: true);$doc->ln(8);
$doc->setFont('helvetica', '', 11);$doc->multiCell(0, 7, 'This document is protected with AES-256 encryption ' . '(standard security handler, revision 6). The user password is required ' . 'to open it; the owner password grants full access. The permission ' . 'bits below are honoured by conforming readers only.');$doc->ln(5);
$permissionTable = [ ['Bit 3 (4)', 'Printing', 'ALLOWED'], ['Bit 4 (8)', 'Content modification', 'DENIED'], ['Bit 5 (16)', 'Text copying / extraction', 'DENIED'], ['Bit 6 (32)', 'Annotations / form fields', 'DENIED'],];$doc->setFont('helvetica', 'B', 10);$doc->cell(30, 7, 'Flag');$doc->cell(60, 7, 'Operation');$doc->cell(0, 7, 'Status', newLine: true);foreach ($permissionTable as [$bit, $operation, $status]) { $doc->setFont('courier', '', 9); $doc->cell(30, 7, $bit); $doc->setFont('helvetica', '', 10); $doc->cell(60, 7, $operation); $doc->setFont('helvetica', 'B', 10); $doc->cell(0, 7, $status, newLine: true);}
$out = getenv('NEXTPDF_COOKBOOK_OUTPUT');$doc->save($out !== false ? $out : __DIR__ . '/encrypted.pdf');
echo "Wrote encrypted PDF (AES-256, printing only)\n";الخرج المتوقع:
Wrote encrypted PDF (AES-256, printing only)عند فتح الملف، يطلب القارئ كلمة مرور. تفتح كلمة مرور المستخدم الملف بمجموعة الأذونات المقيَّدة. وتفتحه كلمة مرور المالك بوصول كامل.
الحالات الحدّية والمزالق
قسم بعنوان «الحالات الحدّية والمزالق»- ترتيب الاستدعاء. استدعاء
setEncryption()بعدaddPage()لا يُشفّر المحتوى السابق بأثر رجعي. هيّئ التشفير أولًا؛ فالمحرك يُشفّر جسم كل كائن أثناء كتابته. - القيمة الافتراضية لكلمة مرور المالك. تجعل كلمة مرور المالك الفارغة المحرك يعيد استخدام كلمة مرور المستخدم ككلمة مرور للمالك. وبذلك لا يبقى عمليًّا دور منفصل ذو امتياز. عيّن كلمتي مرور متمايزتين عندما يلزم فصل الدورين.
- دلالات الأذونات إرشادية. لا تحترم البتات إلا القارئات المطابِقة. وهي لا تُفرَض تشفيريًّا: فأي أداة غير مطابِقة، أو أي أداة تُستخدم مع كلمة مرور المالك، يمكنها تنفيذ العمليات المقيَّدة. تعامل مع الأذونات بوصفها إشارة سياسة موجَّهة إلى البرمجيات المتعاونة، لا بوصفها تحكمًا في الوصول يصمد أمام طرف مُصِرّ.
- لا ضمان للسلامة. التشفير يوفّر السرية لا السلامة. لا يستطيع مهاجم لا يملك كلمة المرور قراءة المحتوى، لكن التنسيق نفسه لا يكتشف العبث. تتطلب حماية السلامة آلية منفصلة، مثل توقيع رقمي أو ISO/TS 32004 document MAC.
- تعارض مع PDF/A. يحظر PDF/A مفتاح المذيّل
Encrypt. إن استدعاءsetEncryption()على مستند PDF/A، بأي ترتيب، يرمي استثناء عدم توافق. - الاشتراك في AES-256-GCM. يختار
useAesGcm()التشفير المُجمَّع ISO/TS 32003 GCM عندما يوفّره OpenSSL أو libsodium المضيف. وإلا، فإنه يرميInvalidConfigException. وهو غير متوافق مع PDF/A للسبب نفسه. - تشفير المفتاح العام غير موصول بعد. يثبّت
setPublicKeyEncryption()سطح واجهة البرمجة API، لكنsave()يرمي استثناءً إلى أن يُنجَز توصيل الكاتب (عيب معروف). لا تستخدمه في بيئة الإنتاج على Core.
الأداء
قسم بعنوان «الأداء»يشغّل اشتقاق المفتاح التجزئة التكرارية في الخوارزمية 2.B مرة واحدة لكل مستند. تشفير AES-256-CBC لكل كائن خطّي في حجم جسم الكائن. بالنسبة إلى المستندات النموذجية، تظل التكلفة ضمن ميزانية 1500 ms / 64 MB بهامش مريح. تتحمّل المستندات الكبيرة جدًّا تكلفة مرتبطة بمعدل إنتاجية AES لكل كائن. نمط Galois/Counter Mode (GCM) مع AES-NI أسرع على المضيفات القادرة.
ملاحظات أمنية
قسم بعنوان «ملاحظات أمنية»- السرية فقط. تكرارًا لحد الثقة: يُبقي التشفير المحتوى بعيدًا عن الأطراف التي لا تملك كلمة المرور. وهو لا يُثبِت أن الملف لم يُعدَّل، وبتات الأذونات معتمِدة على تعاون القارئ.
- قوة كلمة المرور مسؤوليتك. المعالج ليس أقوى من كلمات المرور. بمجرد أن يحصل أحدهم على الملف، يمكن كسر كلمة مرور المستخدم الضعيفة بالقوة الغاشمة دون اتصال؛ ولا يستطيع التنسيق تقييد معدّل المحاولات.
- كلمة مرور المالك مفتاح أساسي. أي شخص يملك كلمة مرور المالك يتجاوز كل قيد. تعامل معها كسر اعتماد جذري؛ لا تضمّنها مع المستند أبدًا ولا تُسجّلها.
#[SensitiveParameter]دفاع متعمّق. يحجب كلمات المرور من تتبّعات مكدّس PHP، لكن لا يزال عليك إبقاؤها خارج سجلّاتك ورسائل الاستثناءات وتقارير الأعطال الخاصة بك.
إقامة البيانات وتدابير التخفيف الخاصة بمعلومات التعريف الشخصية PII
قسم بعنوان «إقامة البيانات وتدابير التخفيف الخاصة بمعلومات التعريف الشخصية PII»تنفّذ المكتبة التشفير داخل العملية. وهي لا تنقل المستند أو كلمات المرور إلى أي مكان. لا يكتب المحرك أي كلمة مرور أو مفتاح أو بايت من المستند إلى القرص باستثناء الخرج المشفَّر الذي تحفظه. إن مكان إقامة ملف الخرج، وكيفية حفظ كلمات المرور، شأنان من شؤون النشر يملكهما المُكامِل. لا تقدّم المكتبة أي ضمان لإقامة البيانات. إذا احتوى المستند النصي الصريح على بيانات شخصية، فإن تلك البيانات ليست أكثر حمايةً من أضعف كلمة مرور ومن مدى التزام القارئ المتعاون المذكور أعلاه. التشفير ليس بديلًا عن تقليل معلومات التعريف الشخصية (PII) التي تضعها في المستند.
القياس عن بُعد الآمن وتنظيف السجلّات
قسم بعنوان «القياس عن بُعد الآمن وتنظيف السجلّات»يُصدِر التشفير حدث EncryptionAppliedEvent يحمل اسم الخوارزمية فقط (AES-256) وثلاث قيم منطقية تلخّص ما إذا كانت print/copy/modify مسموحة — لا تُضاف إلى الحدث أبدًا أي كلمة مرور أو مفتاح أو مِلح أو متجه تهيئة (src/Event/Security/EncryptionAppliedEvent.php). يُمرّر مسار OpenTelemetry سمات النطاق عبر مُنظِّف بقائمة سماح (src/Telemetry/AttributeSanitizer.php) يرفض كلمات المرور ومسارات الملفات دون قيد أو شرط؛ ولا تبقى إلا المفاتيح المُدرَجة في قائمة السماح ذات القيم العددية. لا تُضِف أي مادة متعلقة بكلمة مرور أو مفتاح إلى النطاقات أو السجلّات أو رسائل الاستثناءات في شيفرة التكامل الخاصة بك. تحمي وسوم #[SensitiveParameter] تتبّعات المكدّس، لكنها لا تحمي السلاسل النصية التي تبنيها بنفسك.
نموذج التهديد
قسم بعنوان «نموذج التهديد»ضمن النطاق: خصم يحصل على الملف المشفَّر دون كلمات المرور. لا يستطيع قراءة المحتوى، بحسب قوة كلمة المرور، والملف لا يُسرّب نصًّا صريحًا. خارج النطاق: خصم يملك كلمة مرور المستخدم أو المالك؛ وقارئ غير مطابِق يتجاهل بتات الأذونات؛ وكسر بالقوة الغاشمة دون اتصال لكلمة مرور ضعيفة؛ واكتشاف العبث (يوفّر التشفير السرية لا السلامة)؛ والقنوات الجانبية في بنية OpenSSL المضيفة؛ وإدارة المفاتيح، التي تقع بالكامل على مسؤولية المُكامِل. توثيق هذه التهديدات لا يؤكّد غياب الثغرات.
سلوك وضع FIPS
قسم بعنوان «سلوك وضع FIPS»توفّر بنية OpenSSL المضيفة البدائيات التشفيرية، لذا فإن وضع FIPS خاصية للمضيف، وليس إعدادًا للمكتبة. يُرجِع CryptoCapabilities::detectFipsMode() قيمة FipsModeDetection ثلاثية الحالات (src/Security/FipsModeDetection.php): FIPS_ACTIVE أو FIPS_ABSENT أو INDETERMINATE. لا يُتيح امتداد openssl في PHP أي ربط بنموذج موفِّر OpenSSL 3، لذا فإن الفحص يُنفَّذ بأفضل جهد ممكن. تُعامَل INDETERMINATE على أنها “FIPS غير مُثبَت” (فشل آمن)، ويمكن تمييزها في قياس عن بُعد يستطيع المشغّل اتخاذ إجراء بناءً عليه. لا يدّعي NextPDF اعتماد FIPS 140؛ والتشغيل على OpenSSL معتمَد وفق FIPS مسؤولية المشغّل، ونتيجة الكشف إرشادية.
المطابقة
قسم بعنوان «المطابقة»| العبارة | المواصفة | البند | reference_id |
|---|---|---|---|
يختار رمز قاموس التشفير V خوارزمية التشفير. | ISO 32000-2 | §7.6 | |
يمثّل المُدخَل CFM طريقة مرشّح التشفير AESV3. | ISO 32000-2 | §7.6 | |
المُدخَل P كمية أذونات وصول بطول 32 بت غير مُوقَّع. | ISO 32000-2 | §7.6 | |
| يتحكّم بت الأذونات 3 في الطباعة. | ISO 32000-2 | §7.6 | |
| يتحكّم بت الأذونات 6 في التعليق التوضيحي / تعبئة النماذج. | ISO 32000-2 | §7.6 | |
| يحمي التشفير المحتويات من الوصول غير المصرَّح به (السرية). | ISO 32000-2 | §7.6 | |
| يستخدم اشتقاق المفتاح في المراجعة 6 تجزئة تكرارية مُملَّحة (الخوارزمية 2.B). | ISO 32000-2 | §7.6 | |
| يُعد CBC نمط سرية (لا نمط سلامة). | NIST SP 800-38A | §6.2 | |
| يجب أن تكون متجهات تهيئة CBC غير قابلة للتنبؤ. | NIST SP 800-38A | الملحق C |
يُنفّذ NextPDF البنود المذكورة. وهو لا يؤكّد مطابقة شاملة لـISO 32000-2، ولا اعتماد FIPS 140، ولا أي ضمان قانوني أو تعاقدي للسرية. إن “دعم معالج الأمان القياسي” ليس شهادة أمان في نشرك. يعتمد ذلك على حفظ كلمات المرور وسياسة المُتحقِّق خارج المكتبة.