Nouvelle réglementation de cybersécurité 2026 en vigueur au Maroc. Obtenir un audit de conformité gratuit →
← Retour au blog
OWASP 2025-10-23 ⏱️ 11 min

الروابط الموقّعة مسبقًا ليست تفويضًا: خلل التحكم في الوصول إلى الكائنات في التخزين السحابي

الروابط الموقّعة مسبقًا ليست تفويضًا: خلل التحكم في الوصول إلى الكائنات في التخزين السحابي

تُعد الروابط الموقّعة مسبقًا من أكثر البدائيات فائدة في الهندسة السحابية. فهي تتيح للخادم منح المتصفح رابطًا موقّعًا قصير العمر لرفع أو تنزيل ملف كبير دون تمرير غيغابايتات عبر خادم التطبيق. وهي أيضًا من أسهل الأماكن لإدخال ثغرة IDOR. التوقيع يثبت أن الرابط صادر عن جهة تملك بيانات اعتماد، لكنه لا يثبت أن حامل الرابط مسموح له بالوصول إلى ذلك الكائن. الخلط بين العبارتين أنتج تسريبات بيانات حقيقية في AWS S3 وAzure Blob Storage وGoogle Cloud Storage.


1. ماذا يثبت الرابط الموقّع فعلًا

يضم رابط AWS Signature Version 4 الموقّع معرّف مفتاح الوصول ونطاق بيانات الاعتماد والترويسات الموقّعة والانتهاء وتوقيع HMAC. تعيد خدمة التخزين حساب التوقيع وتتحقق من مطابقة الطلب. إذا كان التوقيع صالحًا والمفتاح مصرّحًا له، ينجح الطلب. لذا فإن الرابط قدرة حامل: من يملكه يستخدمه حتى انتهائه.

هذه الخاصية مقبولة عندما يُولَّد الرابط لكائن المستخدم المصادَق عليه. لكنها تصبح ثغرة عندما يختار العميل مفتاح الكائن:

GET /api/download?key=invoices/2025/INV-1042.pdf HTTP/1.1
Host: app.example.com
Cookie: session=...

إذا وقّع الخادم أي مفتاح يستقبله، يمكن للمستخدم استبداله بمفتاح مستأجر آخر والحصول على رابط صالح. التوقيع أصيل، لكن قرار التفويض لم يُتخذ قط.


2. ثلاثة أنماط فشل شائعة

2.1 مفاتيح كائن يتحكم بها العميل

أكثر خلل مباشر هو نقطة تنزيل أو رفع تقبل مفتاحًا كاملًا وتوقّعه دون التحقق من الملكية. التعداد سهل لأن المفاتيح تتبع غالبًا أنماطًا متوقعة: tenant/TENANT_ID/... أو users/{uuid}/avatar.png أو invoices/2025/INV-{n}.pdf. يطلب المختبر كائنه ثم يستبدل معرّف المستأجر أو UUID المستخدم أو رقم الفاتورة مع إبقاء الجلسة نفسها.

2.2 بيانات اعتماد توقيع واسعة جدًا

حتى عندما يُشتق المفتاح من الخادم، قد تملك هوية التوقيع s3:GetObject على *. يمكن حينها إعادة استخدام رابط وُلّد لكائن مع مسار معدّل إذا لم يغطِّ التوقيع المسار — وهو ما يحدث دائمًا في SigV4، لكن ليس بالضرورة في المخططات المخصصة أو الوكلاء المُعدّين خطأً. والأكثر شيوعًا أن يوقّع الخادم بادئة بدل كائن واحد، أو يعيد للعميل بيانات STS محدودة بكامل الحاوية.

2.3 انتهاء طويل وروابط قابلة لإعادة الاستخدام

يمكن أن تبقى روابط SigV4 صالحة حتى سبعة أيام (604,800 ثانية). الرابط الملصق في تذكرة دعم أو محادثة أو سجل المتصفح يظل صالحًا طوال تلك المدة. وإذا احتوى الكائن على بيانات شخصية، يستمر التعرض بعد الاستجابة للحادث. وتملك رموز SAS في Azure والروابط الموقّعة في Google Cloud أعمارًا قابلة للتعديل مشابهة؛ ويعتمد الأمان كليًا على اختيار عمر قصير.


3. تفاصيل خاصة بكل مزوّد

  • AWS S3: يستخدم مصادقة SigV4 في الاستعلام X-Amz-Algorithm وX-Amz-Credential وX-Amz-Date وX-Amz-Expires وX-Amz-SignedHeaders وX-Amz-Signature. الحد الأقصى للانتهاء سبعة أيام. ترث الروابط صلاحيات الجهة الموقّعة، بما فيها سياسات الجلسة لبيانات الاعتماد المؤقتة.
  • Azure Blob Storage: يرمّز SAS الخدمة أو تفويض المستخدم الصلاحيات (sp) والبداية والانتهاء (st, se) والمورد (sr) والتوقيع (sig). يُفضَّل SAS تفويض المستخدم لأنه مرتبط بهوية Entra ID ويمكن إبطاله بتدوير مفتاح التفويض.
  • Google Cloud Storage: تتضمن روابط V4 الموقّعة X-Goog-Algorithm وX-Goog-Credential وX-Goog-Date وX-Goog-Expires وX-Goog-SignedHeaders وX-Goog-Signature. يجب أن يتم التوقيع بحساب خدمة يملك فقط صلاحية الكائن المطلوبة.

في كل الحالات يغطي التوقيع طريقة HTTP والمضيف والمسار وبعض الترويسات. تغيير المسار يبطل التوقيع. ولهذا بالضبط تعيش الثغرة في التطبيق: فهو من يقرر أي مسار يوقّع.


4. نمط آمن على الخادم

يجب أن يستنتج الخادم الكائن من قرار تفويض، لا من إدخال خام. يشتق مثال Node.js التالي المفتاح من الجلسة المصادَق عليها ومن استعلام قاعدة بيانات:

// Express + AWS SDK v3
import { S3Client, GetObjectCommand } from '@aws-sdk/client-s3';
import { getSignedUrl } from '@aws-sdk/s3-request-presigner';

const s3 = new S3Client({ region: process.env.AWS_REGION });

app.get('/api/invoices/:invoiceId/download', requireAuth, async (req, res) => {
    const invoice = await db.invoices.findOne({
        where: { id: req.params.invoiceId, tenantId: req.user.tenantId }
    });

    if (!invoice) {
        return res.status(404).json({ error: 'Not found' });
    }

    const command = new GetObjectCommand({
        Bucket: process.env.INVOICE_BUCKET,
        Key: invoice.storageKey // قيمة يتحكم بها الخادم من قاعدة البيانات
    });

    const url = await getSignedUrl(s3, command, { expiresIn: 300 });
    return res.json({ url });
});

ثلاث خصائص: المفتاح من قاعدة البيانات، والاستعلام يتضمن معرّف المستأجر، والرابط ينتهي خلال خمس دقائق. من يغيّر invoiceId يحصل على 404 لأن مرشّح الملكية يفشل قبل أي توقيع.

الرفع يحتاج الانضباط نفسه

في الرفع، لا تدع العميل يختار مفتاحًا عشوائيًا. ولّد مفتاحًا على الخادم، وخزّن المالك ونوع المحتوى المقصودين، ووقّع طلب PutObject قصير العمر. وإذا قبل التطبيق اسم ملف من العميل، فطبّعه وأزل فواصل المسار وأضف إليه معرّفًا مولّدًا من الخادم.


5. منهجية الاختبار

  1. ارسم كل نقاط التوقيع. ابحث في حزمة الواجهة والتوثيق عن presign وsigned-url وdownload وupload وattachment وexport.
  2. التقط طلبًا مشروعًا. سجّل المعاملات الدقيقة والرابط المعاد والانتهاء.
  3. غيّر معرّفًا واحدًا في كل مرة. غيّر معرّفات المستأجر وUUID وأسماء الكائنات وأجزاء المسار مع إبقاء ملف تعريف الجلسة.
  4. افحص الاستجابة لا رمز الحالة فقط. استجابة 200 برابط موقّع لكائن آخر اكتشاف حتى لو تعذّر الوصول لاحقًا. فكّ الرابط وقارن المسار.
  5. اختبر الانتهاء. انتظر بعد الموعد المعلن وأعد المحاولة. إذا ظل الرابط يعمل فالخادم لا يطبّق المعامل.
  6. اختبر خلط الطرق. جرّب رابط GET موقّعًا مع PUT أو DELETE. يجب أن يرفضه SigV4؛ وقد لا تفعل ذلك التنفيذات المخصصة.
# فحص معاملات رابط موقّع مسبقًا تم التقاطه
python3 - <<'PY'
from urllib.parse import urlparse, parse_qs
u = urlparse('https://bucket.s3.amazonaws.com/invoices/1042.pdf?X-Amz-Algorithm=...')
for k, v in parse_qs(u.query).items():
    print(k, '=', v[0])
print('path =', u.path)
PY

6. الكشف والتحصين

  • فوّض قبل التوقيع. يجب أن تقبل دالة التوقيع مرجعًا داخليًا لا سلسلة من العميل. واستنتج الملكية في المعاملة نفسها التي تنتج المفتاح.
  • ضيّق بيانات الاعتماد. يجب ألا يملك الموقّع s3:GetObject إلا على البادئة المطلوبة، لا على *. استخدم أدوارًا منفصلة للرفع والتنزيل.
  • أبقِ الانتهاء قصيرًا. خمس إلى خمس عشرة دقيقة تكفي عادة لتنزيل من المتصفح. واحتفظ بالأعمار الطويلة لمهام الدفعات الموثّقة.
  • اربط الروابط بمستخدم أو جلسة حيث أمكن. لا تستطيع الروابط السحابية الأصلية فعل ذلك وحدها؛ ضع رمز تطبيق قصير العمر أمام الكائن، أو بثّ الملف عبر نقطة مصادَق عليها للبيانات الحساسة.
  • سجّل الإصدار والاستخدام. سجّل من طلب أي كائن ومتى صدر الرابط وهل نجح الوصول إلى التخزين. ونبّه عندما تطلب هوية واحدة مستأجرين مختلفين كثيرين في وقت قصير.
  • فضّل تفويض المستخدم على مفاتيح الحساب. في Azure يتجنب SAS تفويض المستخدم تضمين مفتاح حساب في الإعدادات. وفي AWS استخدم بيانات STS مؤقتة بدل مفاتيح IAM طويلة العمر.

7. الخاتمة

الرابط الموقّع مسبقًا تفويض لصلاحية تخزين ضيقة، وليس بديلًا عن فحص التفويض. يجيب التوقيع عن سؤال «هل أنشأ هذا الرابط طرف موثوق؟»، ولا يجيب عن «هل ينبغي لهذا الطرف قراءة هذا الكائن؟». التطبيقات التي تخلط بين السؤالين تنتج ثغرات IDOR لا يراها التحقق من التوقيع وتغفل عنها أدوات الفحص غالبًا. الإصلاح معماري: اشتق مفاتيح الكائنات من سجلات الملكية على الخادم، وحدّد بيانات اعتماد التوقيع بالبادئة الدنيا، وأبقِ العمر قصيرًا كي يبقى الرابط المسرّب حادثًا عابرًا لا تسريبًا دائمًا.

Besoin d'un audit de sécurité ?

Contactez Cayvora pour une consultation gratuite et protégez votre entreprise contre les cybermenaces.

✉️ Contact via Email

Articles connexes