Laravel / المتقدم / الدرس 16
تخزين الملفات الآمن وتنزيلها بإذن
افصل فحص الرفع وهوية التخزين الخاصة والصلاحية والتنزيل كي لا يحل المسار المريح محل الإذن.
في هذه الصفحة
أهداف الدرس
- فحص المحتوى والحجم دون الثقة باسم العميل.
- فصل مفاتيح الخادم من أسماء العرض.
- تفويض كل وصول خاص واختبار فشله.
تحديد الخصوصية قبل اختيار القرص
تقبل التجربة PDF واحدة حتى 2048 كيلوبايت وتخزنها محليًا خارج الويب العام. يحوي Document المستأجر والمالك والمفتاح واسم العرض وتقرر سياسة view الإذن. هذه فروض معلنة لا وصف تطبيق قائم. لا يناسب الربط العام الوثائق السرية، ولا يمنح الاسم غير المتوقع صلاحية. افصل الهوية والعرض والفحص والإذن.
عدم ترك اسم العميل يختار هوية التخزين
يولد التخزين اسمًا ويعيد مفتاحًا نسبيًا؛ احفظه وقرصه بدل بنائه لاحقًا من الطلب. يفحص نموذج PHP معرّفًا عيّنه الخادم ويبني مفتاحًا دون ملف. هو نموذج هوية لا رافع أو مولّد UUID أو إذن، ويجب على مولد الهوية منع التصادم. يبقى الاسم الأصلي بيانات عرض غير موثوقة. لا تحل مسار عميل متسلقًا داخل الخاص.
<?php
declare(strict_types=1);
function documentKey(int $tenantId, string $serverId): string
{
if ($tenantId < 1 || preg_match("/^[a-z0-9-]{1,32}$/D", $serverId) !== 1) {
throw new InvalidArgumentException("invalid storage identity");
}
return "tenant/".$tenantId."/documents/".$serverId.".pdf";
}
echo documentKey(7, "abc-123"), PHP_EOL;
try { documentKey(7, "../private"); }
catch (InvalidArgumentException) { echo "rejected", PHP_EOL; }
tenant/7/documents/abc-123.pdf
rejected
ينتج المعرّف الصحيح المفتاح النسبي ويُرفض شكل التسلق دون فتح مسار أو تعديله.
فحص الرفع قبل التخزين
يشترط المقطع الملف ويفحص MIME المستنتج من المحتوى لـPDF وحجمه. لا يثبت MIME خلو PDF من الضرر أو يستبدل سياسة فحص برمجيات. لا تكفي الامتدادات أو وصف المتصفح. خزّن بعد الإذن والفحص وعالج false أو استثناء الكتابة قبل سجل النجاح. لا تتراجع معاملة قاعدة تلقائيًا عن ملف؛ فحدّد تنظيف الفشل واليتيم. لا ينفذ الدرس هذه العمليات.
<?php
use Illuminate\Validation\Rules\File;
$request->validate([
"attachment" => ["required", File::types(["pdf"])->max(2048)],
]);
$storageKey = $request->file("attachment")->store("reports", "local");
if ($storageKey === false) {
throw new RuntimeException("file storage failed");
}
المتوقع بالتجربة: يعطى PDF المقبول مفتاح reports/ مولدًا ويُرفض المخالف قبل الكتابة. لا يُختلق اسم مولد دقيق.
فحص السياسة قبل كشف البايتات
يستخدم التنزيل Document محلولة وGate قبل البايتات ومفتاحها المحفوظ. لا تجعل المفتاح دليل إذن. قيّد البحث بالمستأجر وقيّم السياسة أيضًا. الرابط المؤقت قدرة محدودة الزمن لها سياسة كشف وصلاحية ولا يستبدل فحص الإذن السابق. افحص دعم المشغل وإعداده. اختر عرض التنزيل ولا تكشف مسارات مطلقة أو أخطاء داخلية أو ترويسات عميل.
<?php
use Illuminate\Support\Facades\Gate;
use Illuminate\Support\Facades\Storage;
Gate::authorize("view", $document);
return Storage::disk("local")->download($document->storage_key, "report.pdf");
المتوقع في اختبار تطبيق: لا يتلقى المرفوض ملفًا وينزل المسموح الكائن باسم عرض ثابت. لا يُنفّذ المقطع هنا.
اختبار الرفض وفشل التخزين ودورة الحياة
يفحص Storage::fake نية الرفع والحذف دون الأقراص الفعلية، وتفحص HTTP السياسة. لا تثبت المحاكاة أذونات مخزن خارجي أو روابطه أو توافره. اختبر عبور المستأجر والمفقود والمحتوى والحجم وفشل الكتابة. حدّد الاحتفاظ والحذف للسجل والكائن والتدقيق والإعادة. لا تسجل البايتات أو المسارات الحساسة، واحفظ الصحة بعد الاستبدال وإلغاء الإذن؛ فالرفع الناجح بداية الدورة فقط.
الأخطاء الشائعة وإصلاحها
وثيقة خاصة تصل عبر رابط عام.
- لماذا يحدث؟
- اختير كشف القرص دون عقد السرية.
- كيف تصلحه؟
- استخدم الخاص ومسار تنزيل مفوضًا.
- كيف تتجنب تكراره؟
- اختبر الوصول المباشر والعابر لا نجاح الرفع فقط.
اسم العميل يؤثر في مسار خاص.
- لماذا يحدث؟
- عوملت بيانات العرض كهوية موثوقة.
- كيف تصلحه؟
- احفظ مفتاح خادم نسبيًا للقرص.
- كيف تتجنب تكراره؟
- افصل اختيار المسار عن أسماء الطلب.
سجل يدعي نجاح الرفع بعد فشل التخزين.
- لماذا يحدث؟
- أُهملت نتيجة الكتابة وفشل الموردين.
- كيف تصلحه؟
- عالج الفشل قبل النجاح ونظف الحالة الجزئية.
- كيف تتجنب تكراره؟
- اختبر فشل الكتابة وسياسة اليتيم.
اختبر فهمك
1. يطلب مستخدم معرّف وثيقة معروفًا لمستأجر آخر. لماذا لا يكفي مفتاح صعب التخمين؟
اعرض الحل المشروح
قد يصل الطلب للسجل دون تخمين المفتاح. افرض النطاق والسياسة قبل التنزيل أو الرابط واختبر معرّفًا أجنبيًا معلومًا دون بايتات. لا تكشف الوجود إذا طلبت السياسة رفضًا غير مميز.
2. تنجح كتابة الملف ويفشل إدراج Document. صف الاستعادة دون ادعاء حذف الملف بالتراجع.
اعرض الحل المشروح
تتبّع المفتاح وحاول تنظيفًا مفوضًا عند فشل السجل. إن فشل التنظيف فسجل مهمة يتيم محدودة قابلة للتسوية. افصل الموردين واختبر فشل السجل والتنظيف وتحقق من غياب نجاح كاذب.
أهم ما تعلمته
- تختلف الهوية والفحص والإذن والتسليم.
- لا تثق باسم العميل كمسار.
- اختبر فشل دورة الملف والسجل منفصلين.