لتأمين تحميلات الملفات في تطبيقات الويب، ضع أذونات صارمة، حدود حجم ومعدل، روابط موقعة قصيرة، وأنماط فحص بسيطة للحد من الحوادث.

يبدو رفع الملفات أمراً غير مؤذي: صورة ملف شخصي، PDF، جدول بيانات. لكنها غالبًا ما تكون الحادث الأمني الأول لأنها تتيح للغرباء إرسال "صندوق غامض" إلى نظامك. إذا قَبِلْتَه، وخزنته، وأعدت عرضه للآخرين، فقد أنشأت طريقًا جديدًا لمهاجمة تطبيقك.
الخطر ليس مجرد "شخص يرفع فيروسا". يمكن لأن يؤدي رفع ضار إلى تسريب ملفات خاصة، زيادة فاتورة التخزين، أو خداع المستخدمين لإعطاء صلاحيات. ملف باسم “invoice.pdf” قد لا يكون PDF على الإطلاق. حتى ملفات PDF والصور الحقيقية قد تسبب مشاكل إذا وثق تطبيقك في بيانات وصفية، أو عرَض المحتوى تلقائيًا، أو قدّمها بقواعد خاطئة.
الفشل الحقيقي عادة ما يبدو هكذا:
تفصيل واحد يسبب الكثير من الحوادث: التخزين ليس نفس التقديم. التخزين هو المكان الذي تحتفظ فيه بالبايتات. التقديم هو كيفية توصيل تلك البايتات إلى المتصفحات والتطبيقات. الأمور تنقلب عندما يقدم التطبيق ملفات المستخدمين بنفس مستوى الثقة وقواعد الموقع الرئيسي، فيعاملها المتصفح كـ"موثوقة".
"آمن بما يكفي" لتطبيق صغير أو نامي عادة يعني أن بإمكانك الإجابة عن أربعة أسئلة دون تهرب: من يمكنه الرفع، ماذا تقبل، ما الحجم والتكرر، ومن يمكنه قراءته لاحقًا. حتى لو تبني بسرعة (بكود مولد أو منصة مدفوعة بالدردشة)، تلك الحواجز ما زالت مهمة.
عامل كل رفع كمدخل غير موثوق. الطريقة العملية لإبقاء التحميلات آمنة هي تصوير من قد يسيء استخدامها وما الذي يُشكّل "نجاحًا" بالنسبة لهم.
معظم المهاجمين إما روبوتات تمسح نماذج رفع ضعيفة أو مستخدمون حقيقيون يدفعون الحدود للحصول على تخزين مجاني، أو جمع بيانات، أو مضايقة خدمتك. أحيانًا يكون المنافس يبحث عن تسريبات أو انقطاعات.
ماذا يريدون؟ عادة أحد هذه النهايات:
ثم اطوِّر نقاط الضعف. نقطة النهاية للرفع هي الباب الأمامي (ملفات كبيرة جدًا، صيغ غريبة، معدلات طلب عالية). التخزين هو الغرفة الخلفية (دلاء عامة، أذونات خاطئة، مجلدات مشتركة). روابط التنزيل هي المخرج (قابلة للتوقع، طويلة الأمد، أو غير مربوطة بمستخدم).
مثال: ميزة "رفع السيرة الذاتية"؛ روبوت يرفع آلاف ملفات PDF كبيرة لرفع التكاليف، بينما مستخدم مسيء يرفع ملف HTML ويشاركه كـ"مستند" لخداع الآخرين.
قبل إضافة ضوابط، قرر ما الأهم لتطبيقك: الخصوصية (من يقرأ)، التوافر (هل يمكنك الاستمرار في الخدمة)، التكلفة (التخزين والنطاق الترددي)، والامتثال (أين تُخزن البيانات وكم تدوم). تلك الأولويات تبقي القرارات متسقة.
معظم حوادث التحميل ليست هجمات متقدمة. هي أخطاء بسيطة "أرى ملف شخص آخر". اعتبر الأذونات جزءًا من التحميلات، لا ميزة تُضاف لاحقًا.
ابدأ بقانون واحد: الرفض الافتراضي. افترض أن كل كائن مرفوع خاص حتى تسمح صراحة بالوصول. "الخصوصية افتراضيًا" هي قاعدة قوية للفواتير، الملفات الطبية، مستندات الحساب، وأي شيء مرتبط بمستخدم. اجعل الملفات عامة فقط عندما يتوقع المستخدم ذلك بوضوح (مثل صورة ملف شخصي عامة)، وحتى ثم فكر في وصول محدود زمنيًا.
اجعل الأدوار بسيطة ومنفصلة. تقسيم شائع:
لا تعتمد على قواعد على مستوى المجلد مثل "كل شيء في /user-uploads/ مقبول". تحقق من الملكية أو وصول المستأجر عند وقت القراءة، لكل ملف. هذا يحميك عندما يغيّر أحد الأشخاص الفرق، أو يغادر منظمة، أو يُعاد تعيين ملف.
نمط جيد للدعم ضيق ومؤقت: امنح وصولًا لملف محدد، سجّله، واجعله ينتهي تلقائيًا.
تبدأ معظم هجمات الرفع بخدعة بسيطة: ملف يبدو آمنًا بسبب اسمه أو هيدر المتصفح، لكنه شيء آخر. عامل كل ما يرسله العميل كغير موثوق.
ابدأ بقائمة سماح: قرر الصيغ الدقيقة التي تقبلها (مثال: .jpg، .png، .pdf) ورفض كل ما عداها. تجنّب "أي صورة" أو "أي مستند" إلا إذا كنت بحاجة فعلًا.
لا تثق بامتداد اسم الملف أو هيدر Content-Type من العميل. كلاهما سهل التزييف. ملف باسم invoice.pdf قد يكون قابلًا للتنفيذ، وContent-Type: image/png قد يكون كذبة.
نهج أقوى هو فحص البايتات الأولى من الملف، المسماة غالبًا "بايتات السحر" أو توقيع الملف. العديد من الصيغ الشائعة لها رؤوس ثابتة (مثل PNG وJPEG). إذا لم يتطابق الرأس مع المسموح، ارفض الملف.
إعداد تحقق عملي:
إعادة التسمية أهم مما يبدو. إذا خزنت أسماء مقدمة من المستخدم مباشرة، فأنت تدعو اختراق المسارات، الأحرف الغريبة، والتلاشي العرضي. استخدم معرفًا مولدًا للتخزين واحتفظ بالاسم الأصلي للعرض فقط.
بالنسبة لصور الملف الشخصي، اقبل JPEG وPNG فقط، تحقق من الرؤوس، واحذف البيانات الوصفية إن أمكن. للمستندات، فكر في تقييد PDF ورفض أي شيء يحتوي على محتوى نشط. إذا قررت لاحقًا أنك بحاجة إلى SVG أو HTML، عاملها كمحتوى قابل للتنفيذ وعزلها.
معظم انقطاعات التحميل ليست "خدعًا متقدمة". هي ملفات ضخمة، طلبات كثيرة، أو اتصالات بطيئة تشغر الخوادم حتى يبدو التطبيق معطلاً. عامل كل بايت كتكلفة.
اختر حدًا أقصى لكل ميزة، لا رقمًا عالميًا واحدًا. الصورة الشخصية لا تحتاج لنفس حد مستند ضريبي أو فيديو قصير. ضع أصغر حد يبقى معقولًا، ثم أضف مسارًا منفصلاً "لتحميلات كبيرة" فقط عندما تحتاج فعلًا.
طبق الحدود في أكثر من مكان لأن العميل يمكنه الكذب: في منطق التطبيق، عند خادم الويب أو البروكسي العكسي، بمهلات الرفع، والرفض المبكر عندما يكون الحجم المصرح به كبيرًا (قبل قراءة الجسم كاملًا).
مثال ملموس: تحديد الصورة الشخصية بحد 2 ميغابايت، PDF بحد 20 ميغابايت، وأي شيء أكبر يتطلب مسارًا مختلفًا (مثلاً رفع مباشر إلى التخزين عبر رابط موقع موقّع).
حتى الملفات الصغيرة قد تتحول إلى DoS إذا رفعها شخص في حلقة. أضف حدود معدل على نقاط نهاية الرفع لكل مستخدم ولكل IP. ضع حدودًا أشد لحركة مجهولة الهوية مقارنة بالمستخدمين المسجلين.
التحميلات القابلة للاستئناف تساعد المستخدمين الحقيقيين على الشبكات الرديئة، لكن يجب أن يكون رمز الجلسة ضيقًا: صلاحية قصيرة، مربوط بالمستخدم، ومقيد بحجم ووجهة ملف محددين. خلاف ذلك تصبح نقاط استئناف أنابيب مجانية إلى التخزين.
عندما تمنع رفعًا، أعد أخطاء واضحة للمستخدم (الملف كبير جدًا، الكثير من الطلبات) لكن لا تكشف معلومات داخلية (مخرجات الأخطاء، أسماء الدلاء، تفاصيل البائع).
التحميلات الآمنة ليست فقط حول ما تقبله. هي أيضًا عن أين يذهب الملف وكيف تعيده لاحقًا.
أبقِ البايتات خارج قاعدة البيانات الرئيسية. معظم التطبيقات تحتاج فقط بيانات وصفية في قاعدة البيانات (معرف مالك المستخدم، اسم الملف الأصلي، النوع المكتشف، الحجم، checksum، مفتاح التخزين، وقت الإنشاء). خزّن البايتات في تخزين كائنات أو خدمة ملفات مُصممة للبلوَبس الكبيرة.
فصّل بين الملفات العامة والخاصة على مستوى التخزين. استخدم دلاء أو حاويات مختلفة بقواعد مختلفة. الملفات العامة (مثل الصور الشخصية العامة) يمكن قراءتها بدون تسجيل دخول. الملفات الخاصة (عقود، فواتير، ملفات طبية) لا يجب أن تكون قابلة للقراءة علنًا حتى لو خمّن شخص الرابط.
تجنّب تقديم ملفات المستخدمين من نفس نطاق تطبيقك عندما تستطيع. إذا تسلل ملف خاطئ (HTML، SVG مع سكريبت، أو غرائب MIME للمتصفح)، استضافته على النطاق الرئيسي قد يتحول إلى استيلاء على الحساب. نطاق تنزيل مخصص أو نطاق تخزين يحد من دائرة الضرر.
في التنزيلات، فرض رؤوس آمنة. عيّن Content-Type متوقعًا بناءً على ما تسمح به، لا ما يدّعيه المستخدم. لأي شيء قد يفسره المتصفح، فضل إرساله كملف للتنزيل.
بعض الافتراضات التي تمنع المفاجآت:
Content-Disposition: attachment للمستندات.Content-Type آمنًا (أو application/octet-stream).الاحتفاظ بالملفات جزء من الأمان أيضًا. احذف التحميلات المهجورة، أزل الإصدارات القديمة بعد الاستبدال، واضبط حدود زمنية للملفات المؤقتة. بيانات أقل مخزنة تعني أقل ما يمكن تسريبه.
الروابط الموقعة (المسماة غالبًا pre-signed URLs) طريقة شائعة للسماح للمستخدمين برفع أو تنزيل ملفات دون جعل دلاء التخزين عامة، ودون إرسال كل بايت عبر API الخاص بك. يحمل الرابط إذنًا مؤقتًا ثم ينتهي صلاحيته.
تدفقان شائعان:
الرفع المباشر يقلل حمل الAPI، لكنه يجعل قواعد التخزين وقيود الرابط أكثر أهمية.
عامل الرابط الموقّع كمفتاح يستخدم لمرة واحدة. اجعله محددًا وقصير الصلاحية.
نمط عملي هو إنشاء سجل رفع أولًا (حالة: pending)، ثم إصدار الرابط الموقّع. بعد الرفع، أكد وجود الكائن ومطابقته للحجم والنوع المتوقع قبل تغيير الحالة إلى جاهز.
تدفق تحميل آمن يعتمد أساسًا على قواعد واضحة وحالة واضحة. عامل كل رفع كغير موثوق حتى تثبت الفحوصات خلاف ذلك.
دوّن ما تسمح به كل ميزة. الصورة الشخصية والمستند الضريبي لا ينبغي أن يتشاركا نفس أنواع الملفات، حدود الحجم، أو الرؤية.
حدّد الأنواع المسموح بها وحد الحجم لكل ميزة (مثال: صور حتى 5 ميغابايت؛ PDF حتى 20 ميغابايت). طبق نفس القواعد في الخلفية.
أنشئ "سجل رفع" قبل وصول البايتات. خزّن: المالك (مستخدم أو منظمة)، الغرض (avatar، invoice، attachment)، اسم الملف الأصلي، الحجم الأقصى المتوقع، وحالة مثل pending.
ارفع إلى موضع خاص. لا تدع العميل يختار المسار النهائي.
تحقّق مرة أخرى على الخادم: الحجم، بايتات السحر/النوع، قائمة السماح. إذا نجح، غيّر الحالة إلى uploaded.
افحص عن برامج خبيثة وحدّث الحالة إلى clean أو quarantined. إذا كان الفحص غير متزامن، أبقِ الوصول مقفولًا أثناء الانتظار.
اسمح بالتنزيل أو المعاينة أو المعالجة فقط عندما تكون الحالة clean.
مثال صغير: للصورة الشخصية، أنشئ سجلًا مرتبطًا بالمستخدم والغرض avatar، خزّنها بشكل خاص، أكد أنها JPEG/PNG فعلًا (ليس مجرد اسم ملف يشبه ذلك)، افحصها، ثم أنشئ رابط معاينة.
فحص البرامج الخبيثة هو شبكة أمان، لا وعد مطلق. يلتقط الملفات السيئة المعروفة والحيل الواضحة، لكنه لن يكتشف كل شيء. الهدف بسيط: تقليل المخاطر وجعل الملفات غير المعروفة غير مؤذية افتراضيًا.
نمط موثوق هو الحجر أولًا. احفظ كل رفع جديد في موقع خاص وغير عام وعلّمه حالة مبدئية. فقط بعد مرور الفحوص انقله إلى موقع "نظيف" أو اجعله متاحًا.
الفحوص المتزامنة تعمل فقط للملفات الصغيرة وحركة منخفضة لأن المستخدم ينتظر. معظم التطبيقات تفحص بشكل غير متزامن: تقبل الرفع، ترجع حالة "قيد المعالجة"، وتفحص في الخلفية.
الفحص الأساسي عادة محرك مضاد فيروسات (أو خدمة) بالإضافة لبعض الحواجز: فحص AV، فحوصات نوع الملف (بايتات السحر)، حدود الأرشيف (قنابل zip، zip متداخلة، أحجام فك ضغط ضخمة)، ومنع الصيغ التي لا تحتاجها.
إذا فشل الماسح، أو انتهت مهلته، أو أعاد "مجهول"، اعتبر الملف مريبًا. احتفظ به في الحجر ولا تمنح رابط تنزيل. هنا تحترق الفرق: "فشل الفحص" لا ينبغي أن يتحول إلى "شحنه على أي حال".
عندما تحجب ملفًا، اجعل الرسالة محايدة: "لم نتمكن من قبول هذا الملف. جرّب ملفًا آخر أو اتصل بالدعم." لا تقل أنك اكتشفت برمجية خبيثة ما لم تكن واثقًا.
اعتبر ميزتين: صورة ملف شخصي (تُعرض علنًا) وإيصال PDF (خاص، يُستخدم للفوترة أو الدعم). كلاهما مشكلات رفع، لكن لا ينبغي أن يتشاركا نفس القواعد.
للصورة الشخصية، اجعل القواعد صارمة: اقبل JPEG/PNG فقط، حد الحجم (مثلاً 2–5 ميغابايت)، وأعد ترميز الصورة على الخادم حتى لا تقدم البايتات الأصلية للمستخدم. خزّنها بشكل عام فقط بعد الفحوص.
لإيصال PDF، اقبل حجمًا أكبر (مثلاً حتى 20 ميغابايت)، اجعلها خاصة افتراضيًا، وتجنب عرضها مضمنة من نطاق التطبيق الرئيسي.
نموذج حالة بسيط يبقي المستخدمين مطّلعين دون كشف البنية الداخلية:
تتناسب الروابط الموقعة هنا: استخدم رابطًا قصير الأمد للرفع (كتابة فقط، مفتاح كائن واحد). أصدر رابط قراءة قصير منفصل للقراءة، فقط عندما تكون الحالة clean.
سجِّل ما تحتاجه للتحقيق، ليس الملف نفسه: معرف المستخدم، معرف الملف، تخمين النوع، الحجم، مفتاح التخزين، الطوابع الزمنية، نتيجة الفحص، معرفات الطلب. تجنّب تسجيل المحتويات الخام أو البيانات الحساسة الموجودة داخل المستندات.
تحدث معظم أخطاء التحميل لأن اختصارًا "مؤقتًا" يصبح دائمًا. افترض أن كل ملف غير موثوق، كل رابط سيُشارك، وكل إعداد "سنصلحه لاحقًا" سينُسى.
الفخاخ المتكررة:
المراقبة هي الشيء الذي يتخطاه الفرق حتى ترتفع فاتورة التخزين. راقب حجم التحميل، متوسط الحجم، أكبر الرافعين، ومعدلات الخطأ. حساب واحد مخترق يمكنه رفع آلاف الملفات الكبيرة خلال الليل بصمت.
مثال: فريق يخزن الصور الشخصية بأسماء يقدمها المستخدم مثل “avatar.png” في مجلد مشترك. مستخدم واحد يبدل صور الآخرين. الإصلاح ممل لكنه فعال: أنشئ مفاتيح كائن على الخادم، اجعل التحميلات خاصة افتراضيًا، وامنح صورة مصغرة عبر استجابة مضبوطة.
استخدم هذه القائمة كفحص نهائي قبل الإطلاق. اعتبر كل بند مانع إطلاق، لأن معظم الحوادث تنجم عن حاجز واحد مفقود.
Content-Type متوقع، أسماء ملفات آمنة، وattachment للمستندات.دوّن قواعدك بلغة بسيطة: الأنواع المسموح بها، الحدود القصوى، من يصل إلى ماذا، كم تعيش الروابط الموقعة، وما معنى "اجتياز الفحص". هذا يصبح العقد المشترك بين المنتج والهندسة والدعم.
أضف بعض الاختبارات التي تلتقط الأخطاء الشائعة: ملفات كبيرة جدًا، تنفيذ ملفات مُعادة التسمية كملفات تنفيذية، قراءات غير مصرح بها، روابط موقعة منتهية، وتنزيل أثناء "قيد الفحص". تلك الاختبارات رخيصة مقارنة بحادث.
إذا كنت تبني وتتكرر بسرعة، يساعدك استخدام سير عمل يمكنك من تخطيط التغييرات والتراجع عنها بأمان. الفرق التي تستخدم Koder.ai (koder.ai) غالبًا ما تعتمد وضع التخطيط واللقطات/الاسترجاع أثناء تشديد قواعد التحميل مع الوقت، لكن المتطلب الأساسي يظل نفسه: إنفاذ السياسة يجب أن يكون في الخلفية، ليس في واجهة المستخدم.
ابدأ بمبدأ الخصوصية كخيار افتراضي واعتبر كل رفع كمدخل غير موثوق. نفّذ أربع قواعد أساسية على الخادم:
إذا أجبت بوضوح على هذه الأسئلة فأنت متقدم على معظم الحوادث.
لأن المستخدمين يمكنهم رفع “صندوق غامض” يخزنه تطبيقك وقد يعيد عرضه لمستخدمين آخرين. ذلك قد يؤدي إلى:
نادراً ما يكون الأمر مجرد "شخص رفع فيروس".
التخزين يعني الاحتفاظ بالبايتات. التقديم يعني تسليم تلك البايتات إلى المتصفحات والتطبيقات.
الخطر يظهر عندما يقدم تطبيقك ملفات المستخدمين بنفس مستوى الثقة وقواعد الموقع الرئيسي. إذا اعتُبر الملف كصفحة عادية قد تنفذ المتصفحات محتواه (أو يثق المستخدمون به أكثر من اللازم).
الافتراض الأكثر أمانًا: خزّن بشكل خاص، ثم قدم عبر استجابات تنزيل مسيطر عليها برؤوس آمنة.
استخدم مبدأ الرفض الافتراضي وتحقق من الوصول في كل مرة يتم فيها تنزيل أو معاينة ملف.
قواعد عملية:
معظم الأخطاء الحقيقية بسيطة: “أرى ملف مستخدم آخر”.
لا تثق بامتداد اسم الملف أو Content-Type المرسل من المتصفح. تحقق على الخادم:
إذا لم تتطابق البايتات مع الصيغة المسموح بها، ارفض الرفع.
غالبًا ما تأتي حالات التوقف من إساءة استخدام بسيطة: ملفات ضخمة، الكثير من الطلبات، أو اتصالات بطيئة تشغّل الخوادم.
إعدادات افتراضية مفيدة:
اعتبر كل بايت كتكلفة وكل طلب كاحتمال إساءة استخدام.
نعم، لكن بحذر. الروابط الموقعة تتيح للمتصفح الرفع/التنزيل مباشرة من التخزين دون جعل الدلو عامًا.
إفتراضات جيدة:
التحميل المباشر للتخزين يقلل حمل API لكنه يجعل تحديد النطاق وانتهاء الصلاحية أمرًا لا تقبل المساومة.
النمط الأكثر أمانًا هو:
pendingclean أو الفحص يساعد لكنه ليس ضمانًا. اعتبره شبكة أمان إضافةً إلى الضوابط الأخرى.
نهج عملي:
المهم هو السياسة: "غير مفحوص" لا يعني "متاح".
قدّم الملفات بطريقة تحول دون تفسيرها كصفحات ويب من قبل المتصفح.
إعدادات افتراضية جيدة:
Content-Disposition: attachment للمستنداتContent-Type آمن يحدده الخادم (أو application/octet-stream)quarantinedcleanهذا يمنع مشاركة ملفات لم يتم فحصها أو التي فشل فحصها عن طريق الخطأ.
هذا يقلل احتمال أن يتحول ملف مرفوع إلى صفحة تصيّد أو تنفيذ سكريبت.