دليل Web Crypto API: تشفير أصلي للمتصفح للمطورين
أتقن Web Crypto API لبناء تطبيقات نقل ملفات مشفرة. دليل شامل لـ AES-GCM وRSA-OAEP وإدارة المفاتيح.
Web Crypto API (المحدَّدة في توصية W3C لواجهة برمجة التطبيقات للتشفير على الويب، ومعرَّضة عبر window.crypto.subtle) هي الطريقة الأصلية للمتصفح لأداء التشفير دون شحن مكتبة تشفير عبر الشبكة. تدعم AES-GCM وAES-CBC وAES-CTR وAES-KW وHMAC وRSA-OAEP وRSA-PSS وRSASSA-PKCS1-v1_5 وECDH وECDSA وHKDF وPBKDF2 عبر جميع المتصفحات الحديثة (Chrome 37+ وFirefox 34+ وSafari 10.1+ وEdge 79+). لتطبيقات نقل الملفات، هذا مهم لأن كل بايت من النص المشفَّر يمكن توليده من جانب العميل قبل الرفع، مع توفير المتصفح تنفيذاً ثابت الوقت ومدقَّقاً. يستعرض هذا الدليل الأوليّات المهمة لنقل الملفات المشفَّر والمزالق التي تعثّر كل تنفيذ أول.
SubtleCrypto قائم على Promise وغير متزامن
كل طريقة على crypto.subtle تُعيد Promise. هذا مقصود: يمكن تفويض عمليات التشفير إلى الأجهزة أو خيوط الخلفية، لذا إجبارها على عدم التزامن يمنع سوء استخدام الـ API بطرق تُعيق الخيط الرئيسي. شكل الكود:
const key = await crypto.subtle.generateKey(
{ name: "AES-GCM", length: 256 },
true, // قابل للاستخراج
["encrypt", "decrypt"]
);
الوسيط الثاني (true) يُعلّم المفتاح قابلاً للاستخراج، مما يعني إمكانية تصديره لاحقاً عبر crypto.subtle.exportKey(). للمفاتيح طويلة الأمد، اضبطه على false لإبقاء البايتات الخام بعيدة عن JavaScript. للمفاتيح التي تحتاج تسلسلها في مقطع URL (نمط HexaTransfer)، اضبطه على true.
الوسيط الثالث مصفوفة استخدامات المفتاح. مفتاح مولَّد بـ ["encrypt"] لا يمكن استخدامه للتشفير العكسي، رغم أن AES-GCM متماثل. هذا الفصل يمنع تدفق تشفير مخترقاً من إساءة استخدامه لفكّ تشفير البيانات التاريخية.
AES-GCM للتشفير المتماثل للملفات
AES-GCM هو العمود الفقري لمحتوى الملفات. يوفر التشفير المصادَق مع البيانات المرتبطة (AEAD): نص مشفَّر وبطاقة مصادقة وبيانات مرتبطة اختيارية تُصادَق لكن لا تُشفَّر. لنقل الملفات، استخدم مفتاحاً بـ 256 بت ومُنبَّهاً بـ 96 بت وفق توصيات NIST SP 800-38D.
const iv = crypto.getRandomValues(new Uint8Array(12)); // مُنبَّه 96 بت
const ciphertext = await crypto.subtle.encrypt(
{ name: "AES-GCM", iv },
key,
plaintext
);
يتضمن الناتج بطاقة مصادقة GCM بـ 128 بت مُلحَقة بالنص المشفَّر. يتحقق فكّ التشفير تلقائياً من البطاقة ويرمي خطأً إذا لم تتطابق. لا تُعِد استخدام مُنبَّه مع المفتاح ذاته؛ خصائص أمان GCM تنهار تدريجياً عند إعادة استخدام المُنبَّه (يستطيع المهاجمون استرداد مفتاح المصادقة). لنقل الملفات حيث يحصل كل ملف على مفتاح جديد، المُنبَّهات العشوائية آمنة.
PBKDF2 للمفاتيح المشتقة من كلمة المرور
حين يكتب المستخدمون كلمة مرور لحماية ملف، لا يمكن استخدامها مباشرةً كمفتاح AES. مرّرها عبر PBKDF2 أولاً:
const passwordKey = await crypto.subtle.importKey(
"raw",
new TextEncoder().encode(password),
"PBKDF2",
false,
["deriveKey"]
);
const aesKey = await crypto.subtle.deriveKey(
{
name: "PBKDF2",
salt: crypto.getRandomValues(new Uint8Array(16)),
iterations: 600000,
hash: "SHA-256",
},
passwordKey,
{ name: "AES-GCM", length: 256 },
false,
["encrypt", "decrypt"]
);
توصيات OWASP 2023 لتجزئة كلمات المرور توصي بـ 600,000 تكرار لـ PBKDF2-SHA-256. أي شيء دون 310,000 يقع دون الممارسات المثلى الحالية. يجب أن يكون الملح عشوائياً ومخزَّناً مع النص المشفَّر (ليس سراً، بل يجب فريده).
للكود الجديد عام 2026، فكّر في Argon2id بدلاً من PBKDF2. Argon2 غير موجود بعد في Web Crypto API، لكن مكتبات كـ argon2-browser أو @noble/hashes توفر تنفيذات JavaScript/WASM. Argon2id يقاوم هجمات GPU بصورة أفضل بكثير من PBKDF2.
RSA-OAEP لتغليف المفاتيح
للسيناريوهات التي تريد فيها تشفير مفتاح AES لملف بالمفتاح العام للمستلم، استخدم RSA-OAEP. توليد المفاتيح:
const keyPair = await crypto.subtle.generateKey(
{
name: "RSA-OAEP",
modulusLength: 4096,
publicExponent: new Uint8Array([1, 0, 1]), // 65537
hash: "SHA-256",
},
true,
["encrypt", "decrypt"]
);
استخدم modulusLength 4096 للمفاتيح الجديدة؛ 2048 مقبول لكن سيبدأ في الإهمال مع تحديد جداول زمنية كمومية. يُشفّر RSA-OAEP الحمولات الصغيرة فقط، لذا غلّف مفتاح AES بـ 256 بت بدلاً من تشفير محتوى الملف مباشرةً.
للتطبيقات الحساسة للأداء، ECDH مع P-256 أو P-384 بديل أفضل من RSA. توليد المفاتيح أسرع بمقدار رتبة من حيث الحجم وأحجام المفاتيح أصغر بكثير.
البث للملفات الكبيرة
ملف بـ 2 جيجابايت لا يناسب ArrayBuffer في المتصفح بيسر. Chrome وFirefox وSafari جميعها تسمح بقراءة الملفات بـ File.stream() التي تُعيد ReadableStream، ثم المعالجة في أجزاء. Web Crypto API ذاتها لا تمتلك بعد طرقاً للتشفير/فكّ التشفير بالبث (هذه ثغرة في المواصفة)، لذا حلّان:
- التقسيم إلى أجزاء (64 كيلوبايت أو 1 ميجابايت) وتشفير كل جزء بمُنبَّه فريد. يُجمع المستلم بالترتيب. هذا يفقد AEAD الحقيقي على الملف كاملاً لكنه يعمل لمعظم الحالات.
- استخدام مكتبة تشفير WASM (libsodium.js أو
@noble/ciphersبنهاية WASM) التي تدعم أوضاع AEAD البثية كـ XChaCha20-Poly1305 أو AES-GCM-SIV.
للنقلات تحت بضع مئات من الميجابايتات، AES-GCM المخزَّن مؤقتاً يعمل بصورة جيدة وأبسط بكثير. فوق ذلك، يصبح البث ضرورياً لتجنب ضغط الذاكرة.
تصدير المفاتيح واستيرادها ومقاطع URL
لتدفقات بنمط HexaTransfer حيث يسافر المفتاح في مقطع URL:
const rawKey = await crypto.subtle.exportKey("raw", aesKey);
const keyBase64 = btoa(String.fromCharCode(...new Uint8Array(rawKey)));
// شارك URL كـ https://example.com/file/abc123#key=keyBase64
مقاطع URL لا تُرسَل إلى الخوادم في طلبات HTTP (المتصفح يجرّدها). هذا يُبقي المفتاح من جانب العميل حتى وإن شارك المستخدم رابطاً. على جانب المستلم:
const keyBase64 = window.location.hash.slice(5); // جرّد "#key="
const rawKey = Uint8Array.from(atob(keyBase64), c => c.charCodeAt(0));
const key = await crypto.subtle.importKey(
"raw", rawKey, "AES-GCM", false, ["decrypt"]
);
استخدم ترميز base64url (استبدل + بـ - و/ بـ _ وجرّد الحشو) لتجنب مشاكل ترميز URL.
المزالق الشائعة
استخدام Math.random() للملاح أو المُنبَّهات. Math.random() ليس آمناً تشفيرياً. استخدم دائماً crypto.getRandomValues().
إعادة استخدام المُنبَّهات مع المفتاح ذاته. خصائص أمان GCM تنهار كلياً عند إعادة استخدام المُنبَّه. المُنبَّهات العشوائية بـ 96 بت تتصادم بعد ~2^48 تشفيراً تحت المفتاح ذاته (حد عيد الميلاد). لنقل الملفات حيث لكل ملف مفتاحه الخاص، آمن؛ لتدفقات الأجزاء، استخدم عداداً.
نسيان HTTPS. crypto.subtle متاحة فقط في السياقات الآمنة (HTTPS أو localhost). على أصل غير آمن، crypto.subtle غير معرَّفة.
تخزين المفاتيح القابلة للاستخراج في IndexedDB دون حماية. إذا احتجت الاحتفاظ بالمفاتيح، غلّفها (مثلاً بمفتاح مشتق من عبارة مرور) قبل التخزين. لا تخزّن مطلقاً مفاتيح AES الخام في localStorage الذي يمكن الوصول إليه من أي سكريبت على الأصل.
الوثوق بكلمات مرور المستخدمين دون PBKDF2. كلمة مرور خام محوَّلة إلى بايتات UTF-8 ليست مفتاحاً بـ 256 بت. اشتق دائماً.
عدم التحقق من بطاقات المصادقة. crypto.subtle.decrypt() يفعل هذا تلقائياً لـ AES-GCM، لكن إذا نفّذت بروتوكولات مخصصة فوق ذلك، لا تُغفل الفحص.
الفروق الدقيقة في دعم المتصفحات
جميع المتصفحات الرئيسية تدعم Web Crypto على HTTPS. بعض التفاصيل:
- كان PBKDF2 في Safari أبطأ من Chrome/Firefox لسنوات؛ سُدَّت الهوّة في Safari 15.
- Firefox يفرض تحققاً أكثر صرامة من المدخلات؛ كود يعمل في Chrome قد يرمي
OperationErrorفي Firefox. اختبر في كليهما. - Web Crypto في عمال الخدمة يعمل لكنه يستلزم أن يكون نطاق التسجيل HTTPS.
- Node.js يوفر
require("crypto").webcryptoمع API متوافق منذ Node 15، مفيد لكود التشفير المتماثل.
متى تستخدم مكتبة بدلاً من ذلك
Web Crypto يغطي الأساسيات بصورة جيدة لكنه يفتقر إلى الأوليّات الحديثة كـ ChaCha20-Poly1305 وArgon2 وX25519 وEd25519 (وإن كان Ed25519 قادماً). لهذه، libsodium.js (عبر WASM) أو @noble/ciphers / @noble/curves (JavaScript خالص، مدقَّق) هي الخيارات الرائدة. HexaTransfer يستخدم أوليّات Web Crypto مباشرةً لـ AES-GCM وPBKDF2 لأنها تغطي مسار نقل الملفات دون تبعيات.
لتدفق نقل مشفَّر كامل، Web Crypto يوصلك في أقل من 100 سطر من الكود: ولّد مفتاح AES، اشتقه أو ولّده عشوائياً، شفّر الملف، ارفع النص المشفَّر، شارك الرابط بالمفتاح في المقطع، يستورد المستلم المفتاح ويفكّ التشفير. هذا كل شيء.
جرّبها على hexatransfer.com — مجاني، بدون حساب، حتى 10 جيجابايت.
أرسل ملفات كبيرة بأمان مع تشفير من طرف إلى طرف
انقل ملفات حتى 10 جيجابايت مجاناً مع تشفير من طرف إلى طرف. لا حاجة لحساب. يتم تشفير ملفاتك في متصفحك قبل الرفع — لا أحد آخر يستطيع قراءتها.
إرسال ملف