تخطَّ إلى المحتوى

افحص توقيعًا موجودًا وافهم حدود الثقة

استخدم أداة الفحص في ⁨Core⁩ للكشف عمّا إذا كان ملف ⁨PDF⁩ يتضمّن قاموس توقيع. تعمل أداة الفحص دون اتصال ولا تستخدم وحدة ⁨Spectrum⁩ الجانبية. توضّح هذه الوصفة أيضًا حدود الثقة: فالكشف عن التوقيع لا يعني التحقق منه. أمّا التحقق التشفيري، والتحقق من مسار الثقة، وفحص الإبطال فهي ميزات في الإصدار ⁨Premium⁩ أو أدوات خارجية.

Terminal window
composer require nextpdf/core:^3

توقيع ⁨PDF⁩ هو حقل توقيع تكون قيمته قاموس توقيع (⁨ISO 32000-2⁩ §12.7.4). يحمل المُدخَل Contents في القاموس بنية ⁨SignedData⁩ الخاصة بصيغة رسائل التشفير (⁨CMS⁩) والمُرمَّزة بترميز ⁨DER⁩ (⁨ISO 32000-2⁩ §12.8.1). يكتشف المسار الاحتياطي السريع في Inspector وجود تلك البنية عبر مسح علامات التوقيع. وهو لا يحلّل ⁨CMS⁩، ولا يعيد حساب موجز نطاق البايتات (الذي يستبعد قيمة التوقيع — ⁨ISO 32000-2⁩ §12.8.1)، ولا يتحقق من سلسلة الشهادات، ولا يفحص الإبطال.

استدعِ new Inspector()، ثم ->inspect(string $pdfData, InspectConfig $config). استخدم InspectConfig::quick() لتشغيل المسار الاحتياطي في ⁨PHP⁩ دون اتصال. يتطلّب InspectDepth::Standard/Full وحدة ⁨Spectrum⁩ الجانبية، ويفشل بأمان (INSPECT-SIDECAR-001) عند غيابها. تكون النتيجة كائن قيمة من النوع InspectResult. في سير العمل هذا، استخدم $hasSigned للكشف عن وجود التوقيع، إلى جانب $isEncrypted و$pdfVersion.

نموذج التعليمات البرمجية — بداية سريعة

قسم بعنوان «نموذج التعليمات البرمجية — بداية سريعة»
<?php
declare(strict_types=1);
require_once __DIR__ . '/vendor/autoload.php';
use NextPDF\Inspect\InspectConfig;
use NextPDF\Inspect\Inspector;
$pdfData = file_get_contents(__DIR__ . '/incoming.pdf');
if ($pdfData === false || $pdfData === '') {
fwrite(STDERR, "Cannot read incoming.pdf\n");
exit(1);
}
$result = (new Inspector())->inspect($pdfData, InspectConfig::quick());
// hasSigned reports the PRESENCE of a signature dictionary.
// It does NOT mean the signature verifies.
echo $result->hasSigned
? "A signature is present — NOT verified.\n"
: "No signature found.\n";

نموذج التعليمات البرمجية — الإنتاج

قسم بعنوان «نموذج التعليمات البرمجية — الإنتاج»

يعمل هذا البرنامج المكتفي ذاتيًا ضمن منصّة اختبار كتاب الوصفات. وهو يحاكي examples/37-inspect-existing-signature.php. يفحص البرنامج عيّنة موقَّعة معروفة من مجموعة المستندات ومستندًا غير موقَّع أُنشئ حديثًا، حتى ترى مساري راية الوجود معًا. ثم يوجّه الحُكم إلى الخطوة التالية. الوجود مُدخَل توجيهي، وليس حُكم ثقة إطلاقًا. سلّم الملف إلى أداة تحقق تشفيري (⁨Pro⁩ أو خارجية). ولا تثق به هنا.

<?php
declare(strict_types=1);
require_once __DIR__ . '/vendor/autoload.php';
use NextPDF\Core\Document;
use NextPDF\Inspect\InspectConfig;
use NextPDF\Inspect\Inspector;
$inspector = new Inspector();
// --- A known-signed input ---
// The repository corpus carries synthetic PAdES samples. In your
// application this is simply the incoming PDF you received.
$signedPath = __DIR__ . '/tests/Corpus/pades/pades-b-b-bytepattern-synthetic.pdf';
if (is_file($signedPath)) {
$signed = (string) file_get_contents($signedPath);
$r = $inspector->inspect($signed, InspectConfig::quick());
echo "Signed sample:\n";
echo ' Signature present : ' . ($r->hasSigned ? 'yes' : 'no') . "\n";
echo ' Encrypted : ' . ($r->isEncrypted ? 'yes' : 'no') . "\n";
echo ' PDF version : ' . ($r->pdfVersion ?? 'unknown') . "\n";
echo " Verdict : presence detected — NOT verified.\n";
if ($r->hasSigned) {
// Presence detected. This is routing input, not a trust verdict.
// Hand the file to a cryptographic verifier (Pro or external)
// before relying on it. (Pseudo-queue shown; wire your own.)
// $verifierQueue->enqueue($signed);
echo " Next step : run a cryptographic verifier before trusting it.\n";
}
} else {
echo "Signed corpus sample absent; skipping the signed branch.\n";
}
// --- A known-unsigned input ---
$unsigned = Document::createStandalone();
$unsigned->setTitle('Unsigned sample');
$unsigned->addPage();
$unsigned->setFont('helvetica', '', 12);
$unsigned->cell(0, 10, 'This document carries no signature.', newLine: true);
$unsignedBytes = $unsigned->getPdfData();
$ru = $inspector->inspect($unsignedBytes, InspectConfig::quick());
echo "Unsigned sample:\n";
echo ' Signature present : ' . ($ru->hasSigned ? 'yes' : 'no') . "\n";
// The harness sets NEXTPDF_COOKBOOK_OUTPUT and runs this script under the
// semantic profile; emit the unsigned document to the side-channel.
$out = getenv('NEXTPDF_COOKBOOK_OUTPUT');
file_put_contents($out !== false && $out !== '' ? $out : __DIR__ . '/inspected.pdf', $unsignedBytes);

مُخرَج ⁨STDOUT⁩ المتوقَّع (يُتخطّى المسار الموقَّع إذا كانت عيّنة مجموعة المستندات غير موجودة):

Signed sample:
Signature present : yes
Encrypted : no
PDF version : <version>
Verdict : presence detected — NOT verified.
Next step : run a cryptographic verifier before trusting it.
Unsigned sample:
Signature present : no
  • الوجود ليس صلاحية. يُبلّغ $hasSigned عن وجود قاموس توقيع. وهو لا يفحص بنية ⁨CMS⁩، ولا موجز نطاق البايتات، ولا شهادة التوقيع، ولا السلسلة، ولا الإبطال. قد يظل ملف مُتلاعَب به يُبلّغ بالقيمة hasSigned = true. لا تعامل الوجود مطلقًا على أنه إثبات للسلامة أو لهوية المُؤلِّف.
  • ما يتطلّبه التحقق الكامل. يعيد القرار الكامل حساب موجز نطاق البايتات (⁨ISO 32000-2⁩ §12.8.1)، ويتحقق من ⁨CMS SignedData⁩، ويبني مسار ⁨X.509⁩ إلى مرساة موثوقة ويتحقق منه، ويفحص الإبطال عبر بروتوكول حالة الشهادة عبر الإنترنت (⁨OCSP⁩) أو قائمة إبطال الشهادات (⁨CRL⁩). أمّا الطابع الزمني للتوقيع، عند وجوده، فيُتحقق منه بدوره مقابل بصمته الخاصة على ثُمانيّات قيمة التوقيع (⁨ETSI EN 319 122-1⁩ §5.3). تعمل هذه العمليات خلف عقود التوقيع. تُشحن تطبيقات الإنتاج في ⁨Pro⁩ و⁨Enterprise.⁩ وتظل أداة التحقق الخارجية هي المسار المدعوم الآخر.
  • عمق الفحص. InspectConfig::quick() هو العمق الوحيد الذي يعمل دون وحدة ⁨Spectrum⁩ الجانبية. يطرح Standard/Full الخطأ INSPECT-SIDECAR-001 عندما لا تكون الوحدة الجانبية متاحة.
  • مُدخَل فارغ. تطرح السلسلة الفارغة استثناء فحص يحمل النص “⁨PDF data must not be empty⁩”. احمِ عملية القراءة بتحقق مسبق.
  • التوقيعات أو الطوابع الزمنية المتعددة. لا تَعُدّ راية الوجود التوقيعات ولا تميّز توقيع الموافقة عن طابع زمني للمستند (الذي يُحمَل أيضًا في unsignedAttrs وفق ⁨RFC 5652⁩ §5.3). استخدم أداة تحقق مخصّصة عندما يكون العدد أو الحُكم لكل توقيع مهمًا.

يُجري المسار الاحتياطي السريع مسحًا محدودًا على بايتات المستند. وهو لا يحلّل مخطط الكائنات الكامل. استخدمه لفرز الملفات الواردة بسرعة قبل توجيهها إلى أداة تحقق أكثر ثقلًا.

أداة الفحص أداة فرز، وليست حدًا للثقة. يجب ألّا تتحكّم قيمة hasSigned الموجبة مطلقًا في قرار ثقة بمفردها.

موقع البيانات وإجراءات الحدّ من المعلومات الشخصية

قسم بعنوان «موقع البيانات وإجراءات الحدّ من المعلومات الشخصية»

يجري الفحص بالكامل داخل العملية. لا تغادر أي بايتات من المستند المضيف. يقرأ المسار الاحتياطي السريع العلامات البنيوية فقط، لا نص المستند؛ لذلك لا يستخرج المعلومات الشخصية القابلة للتعريف (⁨PII⁩) ولا ينقلها.

القياس عن بُعد الآمن وتنقية السجلات

قسم بعنوان «القياس عن بُعد الآمن وتنقية السجلات»

يقبل Inspector أداة تسجيل اختيارية متوافقة مع ⁨PSR-3.⁩ وهو يسجّل المسار المختار (“⁨Spectrum unavailable⁩, ⁨using PHP fallback⁩”)، لا محتوى المستند. لا تسجّل بايتات ملف ⁨PDF⁩ المفحوص أو InspectResult حرفيًا إذا كان المستند حسّاسًا.

المأخوذ في الحسبان: ملف مُتلاعَب به يقدّم قاموس توقيع صحيحًا نحويًا (تُبلّغ أداة الفحص عن الوجود؛ وهي لا تؤكّد السلامة صراحةً)، وملف بلا توقيع (يُبلَّغ عن غيابه بصورة صحيحة). غير المؤكَّد: أن أي توقيع مكتشَف صحيح تشفيريًا أو موثوق أو غير مُبطَل — فذلك من مهام أداة التحقق.

لا يُجري المسار الاحتياطي السريع أي عمليات تشفير، لذلك لا يكون وضع معايير المعالجة الفيدرالية للمعلومات (⁨FIPS⁩) ذا صلة بهذه الوصفة. أمّا التحقق التشفيري (⁨Premium/⁩خارجي) فهو الموضع الذي تهمّ فيه سلسلة مزوّدي ⁨FIPS.⁩

العبارةالمواصفةالبند⁨reference_id⁩
قيمة حقل التوقيع هي قاموس توقيع.⁨ISO 32000-2⁩§12.7.4
يحمل Contents بنية ⁨CMS SignedData⁩ بترميز ⁨DER⁩؛ ويحمل Contents الخاص بطابع زمني للمستند رمز ⁨TimeStampToken.⁩⁨ISO 32000-2⁩§12.8.1
يعيد التحقق حساب الموجز على نطاق البايتات، مع استبعاد قيمة التوقيع.⁨ISO 32000-2⁩§12.8.1
تكون بصمة الطابع الزمني للتوقيع على ثُمانيّات قيمة التوقيع في ⁨SignerInfo.⁩⁨ETSI EN 319 122-1⁩§5.3
يُحمَل الطابع الزمني في ⁨unsignedAttrs⁩ الخاص بـ⁨SignerInfo.⁩⁨RFC 5652⁩§5.3

تكتشف هذه الوصفة وجود توقيع. وهي لا تؤكّد أن أي توقيع صالح أو موثوق أو غير مُبطَل. أداة التحقق التشفيري هي التي تتخذ هذا القرار.

يُشحن التحقق التشفيري من ⁨CMS⁩، والتحقق من مسار ⁨X.509⁩، وفحص الإبطال عبر ⁨OCSP/CRL⁩ خلف عقود التوقيع في إصداري ⁨Pro⁩ و⁨Enterprise.⁩ ولا تغطّي أداة الفحص في ⁨Core⁩ سوى الكشف عن الوجود.