وكيل CORS لجلب الشهادات
تستخدم أداة التوقيع الرقمي وكيل CORS لجلب شهادات الجهات المُصدِرة من جهات إصدار الشهادات (CA) الخارجية. هذا ضروري لأن كثيرًا من خوادم CA لا تتضمن ترويسات CORS في استجاباتها، مما يمنع الجلب المباشر من المتصفح.
إضافة إلى ذلك، تقدّم كثير من خوادم CA الشهادات عبر HTTP عادي. وعندما تكون نسخة BentoPDF لديك مستضافة عبر HTTPS، تحظر المتصفحات هذه الطلبات (سياسة المحتوى المختلط). يحل وكيل CORS المشكلتين بتوجيه الطلبات عبر نقطة HTTPS بترويسات صحيحة.
كيف يعمل
عند توقيع ملف PDF بشهادة:
- تحاول مكتبة
zgapdfsignerبناء سلسلة شهادات كاملة - تجلب شهادات الجهات المُصدِرة من الروابط المضمّنة في امتداد AIA (Authority Information Access) في شهادتك
- تُوجَّه هذه الطلبات عبر وكيل CORS يضيف ترويسات
Access-Control-Allow-Originاللازمة - يعيد الوكيل بيانات الشهادة إلى المتصفح
استضافة وكيل CORS ذاتيًا
إذا كنت تستضيف BentoPDF ذاتيًا، فستحتاج إلى نشر وكيل CORS خاص بك لكي تعمل التوقيعات الرقمية مع الشهادات التي تتطلب جلب السلسلة.
الخيار 1: Cloudflare Workers (موصى به)
ثبّت Wrangler CLI:
bashnpm install -g wranglerسجّل الدخول إلى Cloudflare:
bashwrangler loginانسخ BentoPDF وحدّث المصادر المسموح بها:
bashgit clone https://github.com/alam00000/bentopdf.git cd bentopdf/cloudflareافتح
cors-proxy-worker.jsوغيّر المصفوفةALLOWED_ORIGINSإلى نطاقك:jsconst ALLOWED_ORIGINS = [ 'https://your-domain.com', 'https://www.your-domain.com', ];مهم
بدون هذا التغيير، سيرفض الوكيل كل الطلبات القادمة من موقعك بخطأ 403 Forbidden. الإعداد الافتراضي يسمح فقط بالطلبات القادمة من
bentopdf.com.انشر الوكيل:
bashwrangler deployدوّن رابط الـ Worker الخاص بك (مثل
https://bentopdf-cors-proxy.your-subdomain.workers.dev).أعد بناء BentoPDF مع رابط الوكيل:
إذا كنت تستخدم Docker:
bashexport VITE_CORS_PROXY_URL="https://your-worker.workers.dev" DOCKER_BUILDKIT=1 docker build \ --secret id=VITE_CORS_PROXY_URL,env=VITE_CORS_PROXY_URL \ -t your-bentopdf .إذا كنت تبني من المصدر:
bashVITE_CORS_PROXY_URL=https://your-worker.workers.dev npm run build
الخيار 2: وكيل خلفي مخصص
يمكنك أيضًا إنشاء نقطة وكيل خاصة بك. المتطلبات هي:
- قبول طلبات GET مع معامل الاستعلام
url - جلب الرابط من خادمك (لا توجد قيود CORS على جهة الخادم)
- إرجاع الاستجابة بهذه الترويسات:
Access-Control-Allow-Origin: https://your-domain.comAccess-Control-Allow-Methods: GET, OPTIONSX-Content-Type-Options: nosniff
مثال على تنفيذ باستخدام Express.js:
javascript
app.get('/api/cert-proxy', async (req, res) => {
const targetUrl = req.query.url;
// Validate it's a certificate URL
if (!isValidCertUrl(targetUrl)) {
return res.status(400).json({ error: 'Invalid URL' });
}
try {
const response = await fetch(targetUrl);
const data = await response.arrayBuffer();
res.set('Access-Control-Allow-Origin', 'https://your-domain.com');
res.set('Content-Type', 'application/octet-stream');
res.set('X-Content-Type-Options', 'nosniff');
res.send(Buffer.from(data));
} catch (error) {
res.status(500).json({ error: 'Proxy error' });
}
});اعتبارات الأمان
يتضمن Cloudflare Worker المرفق عدة إجراءات أمنية:
| الميزة | الوصف |
|---|---|
| التحقق من المصدر | يسمح فقط بالطلبات القادمة من النطاقات المدرجة في ALLOWED_ORIGINS |
| قيود الروابط | يسمح فقط بروابط الشهادات (.crt و.cer و.pem و/certs/ و/ocsp و/crl) |
| حظر عناوين IP الخاصة | يحظر النطاقات الخاصة في IPv4/IPv6 وlink-local وloopback وعناوين IP العشرية وبيانات السحابة الوصفية |
| أمان Content-Type | يعيد أنواع MIME الآمنة للشهادات فقط، ويمنع حقن content-type من المصدر |
| حد حجم الملف | يبثّ الاستجابة بحد 10MB ويقطع التنزيل في منتصفه إذا تم تجاوزه |
| تحديد المعدل | 60 طلبًا في الدقيقة لكل IP أو IPv6 /64 (يتطلب KV، ويسمح بالمرور عند أخطاء KV) |
| توقيعات HMAC | توقيع اختياري من جهة العميل (يردع الاستغلال العابر) |
إعادة ربط DNS (عند الاستضافة الذاتية خارج Cloudflare)
يرفض الـ Worker أي رابط يُحلّ إلى عنوان IP خاص أو داخلي. ولا يمكنه استخدام قائمة ثابتة بالمضيفين المسموح بهم، لأن روابط سلسلة الشهادات تشير إلى أي جهة CA أصدرت الشهادة (FNMT وجهات CA المؤسسية وغيرها)، فلا تُعرف الوجهات مسبقًا. وهذا يترك نافذة صغيرة لإعادة الربط: يمكن لمن يدير DNS لنطاقه أن يعطي فحص الأمان عنوان IP عامًا، ثم يعطي الطلب الفعلي عنوانًا خاصًا بعد لحظة.
على Cloudflare لا يمكن استغلال ذلك: لا تستطيع Workers الوصول إلى عناوين IP الخاصة ولا إلى نقطة البيانات الوصفية 169.254.169.254، فتنتهي إعادة الربط إلى لا شيء. فقط لا تمنح الـ Worker طريقًا إلى الداخل: لا تربط Tunnel أو Service أو ربطًا بشبكة خاصة (فهو يحتاج فقط إلى RATE_LIMIT_KV).
إذا شغّلته خارج Cloudflare (workerd أو Node وما شابههما) داخل VPC، فإن بيئة التشغيل تستطيع الوصول إلى عناوين IP الداخلية. ضع جدار حماية للحركة الصادرة أمامه واحظر النطاقات الخاصة: 10.0.0.0/8 و172.16.0.0/12 و192.168.0.0/16 و127.0.0.0/8 و169.254.0.0/16 ونظائرها في IPv6. لا يمكن لكود التطبيق فرض ذلك بمجرد أن تخرج من Cloudflare.
تعطيل الوكيل
إذا كنت لا تريد استخدام وكيل CORS، فاضبط متغير البيئة على سلسلة فارغة:
VITE_CORS_PROXY_URL=ملاحظة: بدون الوكيل، سيفشل التوقيع بالشهادات التي تتطلب جلب سلسلة خارجية (مثل FNMT أو بعض جهات CA المؤسسية) بخطأ "Failed to fetch".
استكشاف الأخطاء وإصلاحها
"Signing error: TypeError: Failed to fetch"
يعني هذا عادةً أحد الأمرين:
- لم يُضبط وكيل CORS: عيّن
VITE_CORS_PROXY_URLوأعد البناء - حظر المحتوى المختلط: موقعك يعمل عبر HTTPS لكن رابط مُصدِر الشهادة يعمل عبر HTTP. يحل وكيل CORS هذه المشكلة.
- وكيل CORS يرفض مصدرك: تأكد من وجود نطاقك في المصفوفة
ALLOWED_ORIGINSفيcors-proxy-worker.js
خطأ "403 Forbidden" من الوكيل
نطاقك غير موجود في قائمة ALLOWED_ORIGINS. عدّل cors-proxy-worker.js:
js
const ALLOWED_ORIGINS = ['https://your-domain.com'];ثم أعد النشر: npx wrangler deploy
اختبار الوكيل
bash
curl -H "Origin: https://your-domain.com" \
"https://your-proxy.workers.dev?url=http://www.cert.fnmt.es/certs/ACUSU.crt"الشهادات التي تعمل بدون وكيل
تتضمن بعض الشهادات السلسلة كاملة في ملف P12/PFX ولا تتطلب جلبًا خارجيًا:
- الشهادات الموقّعة ذاتيًا
- بعض جهات CA التجارية التي تضمّن الشهادات الوسيطة
- الشهادات التي جمعتَ سلسلتها الكاملة يدويًا