Build an Encrypted مشاركة الملفات App from Scratch
Step-by-step tutorial to build an encrypted مشاركة الملفات application. Frontend التشفير, آمن backend, and deployment walkthrough.
بناء تطبيق مشاركة ملفات مُشفَّر يعني وضع التشفير في المتصفح، لا في الخادم. المكدس الأدنى يبدو هكذا: واجهة أمامية مدعومة بـVite وReact تُشفِّر بـAES-256-GCM عبر Web Crypto API، وخلفية Fastify تُعامل كل ملف مرفوع كـblob معتم، وتخزين متوافق مع S3 (Cloudflare R2 أو Backblaze B2)، ورابط مشاركة قصير يوجد فيه مفتاح فك التشفير بعد # حتى لا يصل أبدًا إلى الخادم. يمكنك إطلاق نسخة تعمل في نحو 400 سطر من الكود واستضافتها بأقل من 5 دولارات شهريًا.
قرارات البنية التي تهم فعلًا
القرار الأهم هو أين يعيش مفتاح التشفير. إذا لمس خادمك في أي وقت، ليس لديك تشفير من طرف إلى طرف، بل تشفير من جانب الخادم بخطوات إضافية. النمط الصحيح: ولِّد مفتاحًا عشوائيًا بـ256 بت في المتصفح، شفِّر الملف به، ارفع النص المُشفَّر، وضع المفتاح في جزء URL المجزَّأ (https://yourapp.com/f/abc123#k=base64key). المتصفحات لا ترسل الجزء المجزَّأ في طلبات HTTP، لذا يبقى المفتاح في جانب العميل.
قرار ثانٍ: الرفعات المُجزَّأة. للملفات فوق 100 ميغابايت تحتاج رفعات متعددة الأجزاء قابلة للاستئناف، وإلا يُفسد أي Wi-Fi متقطع عملية النقل. واجهة S3 متعدد الأجزاء تدعم أجزاءً بحد أدنى 5 ميغابايت وحتى 10,000 جزء، مما يمنحك سقفًا بـ50 جيجابايت. خطِّط لذلك منذ البداية.
إعداد هيكل المشروع
ابدأ بحزمتين: واجهة أمامية Vite + React وخلفية Fastify. الواجهة الأمامية تتولى التشفير كاملًا، والخلفية تتولى التخزين والبيانات الوصفية. استخدم TypeScript للحصول على أمان الأنواع على ArrayBuffer وCryptoKey.
pnpm create vite@latest frontend -- --template react-ts
pnpm create fastify backend
أضف هذه الاعتماديات إلى الخلفية: @fastify/multipart و@aws-sdk/client-s3 و@aws-sdk/s3-request-presigner وbetter-sqlite3 لبيانات المشاركة الوصفية. أبقِ مخطط SQLite نحيفًا: shares(id, object_key, size_bytes, expires_at, download_count, max_downloads). لا أسماء ملفات، لا بيانات مستخدمين، لا عناوين IP.
كتابة خط أنابيب التشفير في الواجهة الأمامية
ولِّد مفتاحًا، شفِّر بـAES-256-GCM، وأنتج blob نص مُشفَّر ومفتاحًا بـbase64 لجزء URL المجزَّأ:
async function encryptFile(file: File) {
const key = await crypto.subtle.generateKey(
{ name: 'AES-GCM', length: 256 }, true, ['encrypt', 'decrypt']
);
const iv = crypto.getRandomValues(new Uint8Array(12));
const plaintext = await file.arrayBuffer();
const ciphertext = await crypto.subtle.encrypt(
{ name: 'AES-GCM', iv }, key, plaintext
);
const rawKey = await crypto.subtle.exportKey('raw', key);
const blob = new Blob([iv, new Uint8Array(ciphertext)]);
return { blob, keyBase64: toBase64(rawKey) };
}
للملفات فوق 50 ميغابايت، استبدل هذا بنسخة دفقية تُشفِّر أجزاءً بحجم 4 ميغابايت وتُلحقها بـReadableStream. كومة Safari الجوال تختنق عند أي شيء أكبر من نحو 400 ميغابايت في ArrayBuffer واحد.
تصميم نقطة نهاية الرفع
يجب أن تعرف الخلفية شيئًا مفيدًا. اقبل POST بالنص المُشفَّر، ولِّد معرِّفًا عشوائيًا آمنًا لـURL بـ16 حرفًا، خزِّنه في SQLite مع الانتهاء، وبثَّ الجسم مباشرةً إلى S3:
fastify.post('/upload', async (req, reply) => {
const id = nanoid(16);
const key = `blobs/${id}`;
const upload = new Upload({
client: s3,
params: { Bucket: 'hexa-transfers', Key: key, Body: req.raw }
});
await upload.done();
db.prepare('INSERT INTO shares VALUES (?, ?, ?, ?, 0, ?)').run(
id, key, req.headers['content-length'], Date.now() + 7*86400*1000, 10
);
return { id };
});
انتهاء صلاحية افتراضي 7 أيام، حد أقصى 10 تنزيلات. تريد إعدادات افتراضية متشددة لأن البديل هو نمو التخزين دون حدود. استخدم cron لحذف الـblobs المنتهية ليليًا.
توليد روابط المشاركة بمفاتيح الجزء المجزَّأ
بمجرد اكتمال الرفع، ابنِ رابط المشاركة من جانب العميل:
const { id } = await uploadResponse.json();
const shareUrl = `${location.origin}/f/${id}#k=${keyBase64}`;
هذا الجزء المجزَّأ لا يغادر متصفح المستخدم أبدًا. حين ينقر المستقبِل على الرابط، يقرأ تطبيق React window.location.hash، يُحلِّل المفتاح، يجلب النص المُشفَّر، ويُفكِّك التشفير محليًا. الخادم لا يملك أي مسار للنص الصريح باستثناء دفع JavaScript ضار للمستخدم — وهذا بالضبط سبب ضرورة شحن سياسة أمان محتوى محكمة وتثبيت هاشات الموارد الفرعية.
تطبيق تدفق التنزيل
في صفحة التنزيل، اسحب النص المُشفَّر كتدفق وفكِّك التشفير محاذيًا للأجزاء:
const keyRaw = base64ToBytes(location.hash.slice(3));
const key = await crypto.subtle.importKey(
'raw', keyRaw, { name: 'AES-GCM' }, false, ['decrypt']
);
const resp = await fetch(`/blob/${id}`);
const iv = new Uint8Array(await resp.body.getReader().read().then(r => r.value.slice(0, 12)));
const ct = await resp.arrayBuffer();
const pt = await crypto.subtle.decrypt({ name: 'AES-GCM', iv }, key, ct.slice(12));
const url = URL.createObjectURL(new Blob([pt]));
للملفات الكبيرة، استخدم StreamSaver.js أو File System Access API حتى تصل البايتات المُفكَّكة مباشرةً إلى القرص بدلًا من الذاكرة.
نشر الكل
الواجهة الأمامية: ادفع إلى Cloudflare Pages أو Vercel، كلاهما يتعامل مع ذلك بالمستويات المجانية. الخلفية: droplet واحدة بـ5 دولارات من DigitalOcean تشغِّل Node 22 خلف Caddy (TLS 1.3 تلقائي). التخزين: Cloudflare R2 يمنحك رسوم خروج صفرية، وهي الميزة القاتلة لمشاركة الملفات بالحجم. WeTransfer وSmash وSwissTransfer يحترقون بثروة من خروج AWS؛ R2 يُغيِّر الاقتصاديات كليًا.
اضبط ترويسات صارمة عبر Caddy: Strict-Transport-Security وContent-Security-Policy: default-src 'self'; script-src 'self' وX-Content-Type-Options: nosniff وReferrer-Policy: no-referrer. هذه تُغلق الناقلات الشائعة لتسرب جزء URL عبر المُحيلين أو السكريبتات المحقونة.
جعل التطبيق متوافقًا مع RGPD بشكل افتراضي
لأن الخادم لا يرى إلا النص المُشفَّر، ليس لديك تقريبًا أي شيء يُؤهَّل كبيانات شخصية بموجب RGPD المادة 4. مع ذلك، وثِّق علاقات المعالجين (R2 وDigitalOcean)، اضبط احتفاظ بالـblobs بحد أقصى 30 يومًا، وانشر إشعارًا واضحًا بأن روابط المشاركة تحتوي على مفاتيح ويجب نقلها عبر قنوات يثق بها المُرسِل. أضف تقييد معدل عند 50 رفعة لكل IP في الساعة لتثبيط الإساءة دون تسجيل هويات المستخدمين.
HexaTransfer مبنية على هذه البنية بالأساس — Web Crypto في المتصفح، وتخزين blob معتم، ومفاتيح في أجزاء URL، وصفر نص صريح على الخوادم. جرِّبها على hexatransfer.com — مجانًا، بدون حساب، بحد أقصى 10 جيجابايت.
ما تُضيفه بمجرد عمل النسخة الأولى
بمجرد شحن التدفق الأساسي، الإضافات الأعلى قيمةً هي: حماية بكلمة مرور فوق مفتاح الجزء المجزَّأ (PBKDF2 مع 600,000 تكرار)، وإشعارات بريد لكل تنزيل عبر صندوق بريد مؤقت، وإبلاغ عن الإساءة يتيح وسم هاش النص المُشفَّر دون الكشف عن المحتوى. تجنَّب إضافة حسابات ما لم يكن لديك سبب محدد — تُحوِّل تطبيقك من أداة خصوصية إلى مسؤولية بيانات بين عشية وضحاها. أطلِق صغيرًا، ابقَ ذا رأي، ودع التشفير يتولى العمل الثقيل.
أرسل ملفات كبيرة بأمان مع تشفير من طرف إلى طرف
انقل ملفات حتى 10 جيجابايت مجاناً مع تشفير من طرف إلى طرف. لا حاجة لحساب. يتم تشفير ملفاتك في متصفحك قبل الرفع — لا أحد آخر يستطيع قراءتها.
إرسال ملف