رفض الوصول إلى صورة S3 العامة: لماذا يتجاوز Block Public Access إعداد ACL للكائن؟

رفعت صورة إلى S3، ضبطت الكائن على 'عام'، ثم فتحت الرابط المباشر — فظهر لك AccessDenied. المشكلة ليست في صلاحية الكائن نفسه، بل في طبقة تحكم أعلى منه تُلغي كل إعدادات ACL قبل أن يُقيّمها S3 أصلاً، وهي ميزة Block Public Access.

TL;DR — ملخص سريع

الطبقةما تفعلهالأولوية
Block Public Access على مستوى الحسابتمنع أي وصول عام لجميع الـ Buckets في الحسابالأعلى
Block Public Access على مستوى الـ Bucketتمنع الوصول العام لهذا الـ Bucket تحديداًثانية
Bucket Policyتُحدد من يصل وبأي شروطتُقيَّم فقط إذا لم يمنعها BPA
Object ACLتُحدد صلاحية الكائن الفرديتُقيَّم فقط إذا لم يمنعها BPA

كيف يعمل Block Public Access في S3

عندما يصل طلب إلى S3، لا يبدأ التقييم من سياسة الـ Bucket أو ACL الكائن — بل يبدأ من إعدادات Block Public Access. هذه الإعدادات تعمل كحاجز خارجي يُقيَّم قبل أي منطق آخر. إذا كان أي من الإعدادات الأربعة مُفعَّلاً على مستوى الحساب أو الـ Bucket، يرفض S3 الطلب العام فوراً دون النظر في سياسات الـ Bucket أو ACLs على الإطلاق.

الإعدادات الأربعة لـ Block Public Access هي:

  • BlockPublicAcls — يمنع تعيين ACLs عامة جديدة على الـ Bucket أو الكائنات.
  • IgnorePublicAcls — يتجاهل جميع ACLs العامة الموجودة، حتى لو كانت مضبوطة على 'public-read'.
  • BlockPublicPolicy — يمنع إضافة سياسات Bucket تمنح وصولاً عاماً.
  • RestrictPublicBuckets — يقيّد الوصول العام حتى لو كانت سياسة الـ Bucket تسمح به.

الإعداد الذي يُسبب مشكلتك غالباً هو IgnorePublicAcls — فحتى لو ضبطت الكائن على public-read، يتجاهل S3 هذا الإعداد تماماً طالما هذا الخيار مُفعَّل.

graph TD A["طلب وصول عام
GET Object"] --> B{"Block Public Access
على مستوى الحساب؟"} B -- "مُفعَّل" --> R1["❌ رفض فوري
AccessDenied"] B -- "مُعطَّل" --> C{"Block Public Access
على مستوى الـ Bucket؟"} C -- "مُفعَّل" --> R2["❌ رفض فوري
AccessDenied"] C -- "مُعطَّل" --> D{"هل توجد
Bucket Policy؟"} D -- "نعم" --> E{"هل تسمح
Bucket Policy؟"} E -- "نعم" --> G["✅ وصول مسموح"] E -- "لا" --> R3["❌ رفض
AccessDenied"] D -- "لا" --> F{"هل ACL الكائن
public-read؟"} F -- "نعم" --> G F -- "لا" --> R4["❌ رفض
AccessDenied"] style R1 fill:#ff4444,color:#fff style R2 fill:#ff4444,color:#fff style R3 fill:#ff4444,color:#fff style R4 fill:#ff4444,color:#fff style G fill:#00aa44,color:#fff
  1. Block Public Access على مستوى الحساب: أول نقطة تحقق. إذا كان مُفعَّلاً، يُرفض الطلب فوراً لجميع الـ Buckets في الحساب.
  2. Block Public Access على مستوى الـ Bucket: إذا اجتاز الطلب مستوى الحساب، يُفحص إعداد الـ Bucket المحدد. أي إعداد BPA مُفعَّل هنا يرفض الطلب.
  3. تقييم Bucket Policy و ACLs: فقط إذا كانت جميع إعدادات BPA مُعطَّلة على كلا المستويين، يبدأ S3 في تقييم سياسة الـ Bucket وACL الكائن لتحديد الصلاحية النهائية.

تشخيص مشكلة رفض الوصول إلى S3 خطوة بخطوة

الخطوة 1: فحص Block Public Access على مستوى الحساب

قبل فحص الـ Bucket نفسه، تحقق من إعدادات الحساب — فهي تُلغي إعدادات الـ Bucket بالكامل. كثير من المهندسين يقضون وقتاً في تعديل سياسات الـ Bucket دون أن يدركوا أن الحساب نفسه يمنع أي وصول عام.

aws s3control get-public-access-block \
  --account-id 123456789012

إذا أعاد أي من الحقول الأربعة قيمة true، فهذا هو السبب الجذري. لتعطيله على مستوى الحساب:

aws s3control put-public-access-block \
  --account-id 123456789012 \
  --public-access-block-configuration BlockPublicAcls=false,IgnorePublicAcls=false,BlockPublicPolicy=false,RestrictPublicBuckets=false

تحذير: تعطيل BPA على مستوى الحساب يؤثر على جميع الـ Buckets. قيّم هذا القرار بعناية وفق متطلبات الأمان لديك.

الخطوة 2: فحص Block Public Access على مستوى الـ Bucket

إذا كانت إعدادات الحساب سليمة، انتقل إلى الـ Bucket المحدد. هذا هو الإعداد الذي يُفعَّل افتراضياً عند إنشاء Bucket جديد منذ عام 2023.

aws s3api get-public-access-block \
  --bucket your-bucket-name

إذا أعاد IgnorePublicAcls: true أو RestrictPublicBuckets: true، فهذا يُفسر لماذا لا تعمل ACL الكائن. لتعطيل BPA على مستوى الـ Bucket فقط:

aws s3api put-public-access-block \
  --bucket your-bucket-name \
  --public-access-block-configuration BlockPublicAcls=false,IgnorePublicAcls=false,BlockPublicPolicy=false,RestrictPublicBuckets=false

الخطوة 3: التحقق من ACL الكائن

بعد التأكد من تعطيل BPA، تحقق من أن ACL الكائن مضبوط فعلاً على public-read. في كثير من الحالات، يظن المهندس أنه ضبط الكائن على 'عام' من الواجهة، لكن الإعداد لم يُحفظ بسبب وجود BPA وقت الرفع.

aws s3api get-object-acl \
  --bucket your-bucket-name \
  --key path/to/your-image.jpg

إذا لم يظهر AllUsers في قائمة المنح، اضبط ACL الكائن يدوياً:

aws s3api put-object-acl \
  --bucket your-bucket-name \
  --key path/to/your-image.jpg \
  --acl public-read

الخطوة 4: استخدام Bucket Policy بدلاً من ACL (النهج المُوصى به)

الاعتماد على Object ACLs لتوفير الوصول العام نهج قديم. AWS تُوصي باستخدام Bucket Policy لأنها أوضح وأسهل في التدقيق. هذا النهج يعمل حتى لو كانت BlockPublicAcls وIgnorePublicAcls مُفعَّلتين، طالما أن BlockPublicPolicy وRestrictPublicBuckets مُعطَّلتان.

🔽 انقر لعرض سياسة Bucket للوصول العام للقراءة
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "PublicReadGetObject",
      "Effect": "Allow",
      "Principal": "*",
      "Action": "s3:GetObject",
      "Resource": "arn:aws:s3:::your-bucket-name/*"
    }
  ]
}
aws s3api put-bucket-policy \
  --bucket your-bucket-name \
  --policy file://bucket-policy.json

الخطأ الشائع: الخلط بين إعداد الكائن وإعداد الـ Bucket

السيناريو الذي يتكرر كثيراً: مهندس يرفع صورة، يضغط 'Make Public' من AWS Console، يحصل على AccessDenied، ثم يذهب إلى إعدادات الـ Bucket ويُعطّل BPA، ويُعيد المحاولة — لكن المشكلة تستمر.

السبب: عند ضغطه على 'Make Public' وكان BPA مُفعَّلاً، لم تُحفظ ACL الكائن أصلاً. بعد تعطيل BPA، الكائن لا يزال بدون ACL عامة. يجب إعادة ضبط ACL الكائن بعد تعطيل BPA.

Block Public Access يشبه قفل رئيسي على باب المبنى — حتى لو كان مفتاح الغرفة الداخلية معك، القفل الرئيسي يمنعك من الدخول قبل أن تصل إليه.
graph LR subgraph Case1 ["الحالة 1: BPA مُفعَّل"] A1["طلب GET"] --> B1["BPA: IgnorePublicAcls=true"] B1 --> C1["ACL الكائن مُتجاهَلة"] C1 --> D1["❌ AccessDenied"] end subgraph Case2 ["الحالة 2: BPA مُعطَّل + ACL"] A2["طلب GET"] --> B2["BPA: مُعطَّل"] B2 --> C2["ACL: public-read"] C2 --> D2["✅ وصول مسموح"] end subgraph Case3 ["الحالة 3: BPA مُعطَّل + Bucket Policy"] A3["طلب GET"] --> B3["BPA: مُعطَّل"] B3 --> C3["Bucket Policy: Allow *"] C3 --> D3["✅ وصول مسموح"] end style D1 fill:#ff4444,color:#fff style D2 fill:#00aa44,color:#fff style D3 fill:#00aa44,color:#fff
  1. الحالة الأولى (BPA مُفعَّل): حتى مع ACL عامة على الكائن، يرفض S3 الطلب لأن IgnorePublicAcls يتجاهل ACL قبل تقييمها.
  2. الحالة الثانية (BPA مُعطَّل + ACL عامة): الطلب يصل إلى مرحلة تقييم ACL ويُمنح الوصول.
  3. الحالة الثالثة (BPA مُعطَّل + Bucket Policy): النهج الأنظف — لا اعتماد على ACLs الكائنات الفردية.

IAM Policy للتشخيص والإدارة

إذا كنت تُشخّص هذه المشكلة بصلاحيات محدودة، تحتاج على الأقل إلى الصلاحيات التالية:

🔽 انقر لعرض IAM Policy للتشخيص
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "S3PublicAccessDiagnostics",
      "Effect": "Allow",
      "Action": [
        "s3:GetBucketPublicAccessBlock",
        "s3:PutBucketPublicAccessBlock",
        "s3:GetObjectAcl",
        "s3:PutObjectAcl",
        "s3:GetBucketPolicy",
        "s3:PutBucketPolicy",
        "s3control:GetPublicAccessBlock",
        "s3control:PutPublicAccessBlock"
      ],
      "Resource": [
        "arn:aws:s3:::your-bucket-name",
        "arn:aws:s3:::your-bucket-name/*"
      ]
    },
    {
      "Sid": "S3AccountLevelAccess",
      "Effect": "Allow",
      "Action": [
        "s3control:GetPublicAccessBlock",
        "s3control:PutPublicAccessBlock"
      ],
      "Resource": "arn:aws:s3control::123456789012:account/123456789012"
    }
  ]
}

لاحظ أن s3control:GetPublicAccessBlock وs3control:PutPublicAccessBlock على مستوى الحساب يتطلبان مورداً مختلفاً عن صلاحيات الـ Bucket العادية — خطأ شائع عند كتابة سياسات IAM لهذا السيناريو.

الخلاصة والخطوات التالية لحل رفض الوصول في S3

جذر مشكلة رفض الوصول في S3 لملفات عامة يكون في معظم الحالات في إعدادات Block Public Access، وليس في ACL الكائن. الترتيب الصحيح للتشخيص: الحساب أولاً، ثم الـ Bucket، ثم الكائن. وإذا كنت تبني نظاماً جديداً، استخدم Bucket Policy بدلاً من Object ACLs — فهي أوضح في التدقيق وأكثر توافقاً مع ممارسات AWS الحديثة.

مسرد المصطلحات

المصطلحالتعريف
Block Public Access (BPA)مجموعة إعدادات S3 تمنع الوصول العام على مستوى الحساب أو الـ Bucket، تُقيَّم قبل أي سياسة أو ACL
Object ACLقائمة تحكم وصول مرتبطة بكائن S3 فردي تُحدد من يمكنه قراءته أو كتابته
Bucket Policyسياسة JSON مرتبطة بـ Bucket تُحدد قواعد الوصول لجميع الكائنات داخله
IgnorePublicAclsإعداد BPA يتجاهل جميع ACLs العامة الموجودة على الـ Bucket والكائنات
RestrictPublicBucketsإعداد BPA يقيّد الوصول العام حتى لو كانت Bucket Policy تسمح به صراحةً

تعليقات

المشاركات الشائعة من هذه المدونة

استرجاع معرّف نسخة EC2 عبر خدمة البيانات الوصفية IMDSv2

أوضاع سعة DynamoDB: متى تختار On-Demand ومتى تختار Provisioned؟

فهم مهلة الرؤية في SQS: لماذا تُعالَج رسائلك مرتين؟