تصميم نظام رفع الملفات الآمن: روابط S3 الموقعة مسبقاً وفحص الهوية الحقيقية
تأمين وتسريع رفع الملفات عبر الرفع المباشر لخدمات التخزين السحابي مع الروابط الموقعة والتحقق من البايتات السحرية والفحص الأمني.
🖼️ Upload Image: إزاي تبني File Upload System صح؟
سؤال انترفيو:
</div>المستخدم ضغط Upload Image واختار صورة وبعت الـRequest... إيه اللي المفروض يحصل؟
📚 Table of Contents
- المشكلة مش في رفع الصورة
- أول قرار: الصورة هتتحفظ فين؟
- Database Storage
- Local Filesystem
- Media/File Server
- Object Storage
- CDN
- Never Trust The Client
- File Type Validation
- File Size Limits
- Unique File Names
- Path Traversal
- Image Processing Security
- SVG Security
- Image Dimensions
- Private vs Public Files
- Access Control
- Orphan Files
- Upload Architecture
- Direct-to-Storage Upload
- Processing Pipeline
- Database Design
- Failure Scenarios
- Common Wrong Answers
- Interview Answer
- Production Checklist
- Official Sources
🎤 السؤال
المستخدم ضغط:
Upload Image
واختار صورة.
وبعت الـRequest.
إيه اللي المفروض يحصل؟
أغلب الناس هتقول:
استلم الصورة
↓
احفظها
↓
خلصنا
لكن في الـproduction الموضوع أكبر بكتير.
لأنك لازم تجاوب على أسئلة زي:
Where?
How big?
What type?
Who owns it?
Who can access it?
How long should it live?
How do I process it?
How do I prevent abuse?
How do I scale it?
How do I delete it?
🧠 أول قرار: الصورة هتتحفظ فين؟
عندك اختيارات كثيرة:
Database
Local Filesystem
Media/File Server
Object Storage
Object Storage + CDN
والاختيار مش مجرد preference.
ده:
Architecture Decision
🗄️ Database Storage
ممكن تخزن الـbinary نفسه داخل Database.
الفكرة مغرية:
Application
↓
Database
├── Users
├── Orders
└── Images
مميزات:
- Backup واحد
- Transaction boundaries أسهل في بعض السيناريوهات
- البيانات والـmetadata في مكان واحد
- Access control ممكن يكون مرتبط بالـDB
لكن تخيل:
100,000 users
×
5 images
=
500,000 images
وفجأة قاعدة البيانات نفسها بقت ضخمة جدًا.
وده ممكن يأثر على:
- Backup
- Restore
- Replication
- Storage cost
- Database maintenance
- I/O
- Operational complexity
والأهم:
Database مش معمول بالضرورة عشان يكون أفضل blob store لكل أنواع الملفات.
وده لا يعني إن تخزين الملفات في DB غلط دائمًا.
في بعض الأنظمة والـuse cases ممكن يكون منطقي.
لكن لازم يكون قرار مقصود.
💾 Local Filesystem
حل بسيط جدًا:
project/
└── uploads/
├── image-1.jpg
├── image-2.png
└── image-3.webp
ممتاز كبداية لمشروع صغير.
لكن مع الوقت:
10 GB
20 GB
50 GB
100 GB
تبدأ مشاكل:
- Deployment
- Backup
- Disk space
- Server migration
- Disaster recovery
- Multiple instances
والكارثة الأكبر:
Server 1
uploads/
image-a.jpg
Server 2
uploads/
❌ image-a.jpg
لو المستخدم رفع على Server 1 وبعدها request تاني راح Server 2:
Where is the image?
🖥️ Media/File Server
حل وسط:
App Server 1 ─┐
App Server 2 ─┼──> Media Server
App Server 3 ─┘
كل الـapplication servers يستخدموا storage مركزي.
وده يحل مشكلة:
Server 1 ≠ Server 2
لكن لسه عندك تحديات:
- Scaling
- High availability
- Backup
- Network dependency
- Storage management
- Disaster recovery
يعني أنت بدأت تبني storage infrastructure بنفسك.
☁️ Object Storage
في الأنظمة الكبيرة غالبًا تستخدم:
- Amazon S3
- Azure Blob Storage
- Google Cloud Storage
الفكرة:
Application
↓
Object Storage
↓
Images / Videos / Documents
والـdatabase تحتفظ بالـmetadata فقط.
مثلاً:
users
وفيها:
{
"id": 123,
"avatarKey": "users/123/avatar-8f32.webp"
}
مش لازم تخزن الـbinary نفسه داخل الـdatabase.
⚡ CDN
دلوقتي عندك:
User
↓
CDN
↓
Object Storage
بدل:
User in Egypt
↓
Storage in Europe
كل request يروح للـedge الأقرب قدر الإمكان.
والـCDN ممكن يعمل:
Cache Hit
فتاخد الصورة من edge cache.
ولو:
Cache Miss
يرجع للمصدر.
النتيجة:
Lower latency
+
Less origin traffic
+
Better scalability
🔐 Public vs Private Storage
دي نقطة مهمة جدًا.
مش كل الصور Public.
مثلاً:
Public
Product image
Blog image
Public avatar
ممكن تستخدم:
CDN
+
Public URL
Private
National ID
Medical document
Invoice
Private attachment
User document
مينفعش تقول:
https://storage.example.com/user-123/document.pdf
وتخلي أي حد عنده الرابط يقرأ الملف لو هو المفروض Private.
هنا تحتاج:
Authorization
+
Signed URL
أو:
Authenticated download endpoint
🚨 Never Trust The Client
دي من أهم قواعد الـfile uploads:
Never trust the client.
المستخدم بعت:
image.jpg
هل ده معناه إنه image؟
❌ لا.
الـfilename مجرد input من المستخدم.
المستخدم ممكن يغير:
malware.exe
إلى:
cute-cat.jpg
فالاعتماد على extension فقط غير كافٍ.
🔍 File Type Validation
لازم تعمل validation حقيقي.
مثلاً تتحقق من:
Declared MIME type
+
File signature / magic bytes
+
Parser behavior
والـOWASP توصي بعدم الاعتماد على Content-Type الذي يرسله المستخدم لأنه يمكن تزويره، وباستخدام أكثر من طبقة validation. citeturn0search0
مثال:
User says:
image/png
Actual bytes:
Something else
هنا:
Reject
📏 File Size Limits
تخيل:
POST /upload
والمستخدم بعت:
5 GB
بدون limits.
ممكن يحصل:
Memory exhaustion
Disk exhaustion
Bandwidth abuse
Long request duration
DoS
عشان كده لازم تحدد:
Max request size
Max file size
والأفضل يكون الحد enforced مبكرًا قدر الإمكان، مش بعد ما تكون استقبلت الملف كامل.
🧬 Unique File Names
المستخدم بعت:
profile.jpg
وبعده مستخدم تاني:
profile.jpg
لو استخدمت الاسم الأصلي:
uploads/profile.jpg
ممكن يحصل:
Overwrite
عشان كده الأفضل تولد identifier خاص بك:
UUID
أو random object key.
مثلاً:
users/123/7f6c0e2a-...-avatar.webp
والـOWASP توصي بإعادة تسمية الملفات باستخدام اسم يولده التطبيق بدل الاعتماد على اسم المستخدم، مع تجنب السماح للمستخدم بتحديد مسار التخزين. citeturn0search0
🛣️ Path Traversal
دي من أخطر المشاكل.
لو أنت بتعمل:
save(uploadDir + "/" + userProvidedFilename)
والمستخدم بعت:
../../../config.json
فأنت عندك مشكلة.
المهاجم بيحاول يخرج من:
uploads/
إلى:
application/
system/
config/
وده اسمه:
Path Traversal
OWASP تعتبر filename من مدخلات المستخدم غير الموثوقة، وتحذر من استخدامه مباشرة في filesystem paths. citeturn0search0turn0search4
الحل الأفضل:
متخليش المستخدم يحدد:
path
filename
storage location
أنت اللي تحدد:
storage key
🧨 Image Processing Security
افترض إن الملف فعلاً صورة.
لسه مش بالضرورة آمن.
أنت ممكن تعمل:
Resize
Crop
Thumbnail
Watermark
Compression
Format conversion
وده معناه إنك بتشغل parser / image-processing library على untrusted input.
لو المكتبة فيها vulnerability:
Malicious Image
↓
Image Parser
↓
Vulnerability
↓
Potential compromise
عشان كده:
- استخدم مكتبات محدثة
- راقب CVEs
- حدّث image processing dependencies
- افصل المعالجة عن الـmain application عندما يكون risk عالي
- ضع limits للـresource consumption
OWASP توصي أيضًا باستخدام libraries حديثة وتحديثها باستمرار، مع فحص الملفات عند الحاجة. citeturn0search0
⚠️ SVG Security
ناس كتير تقول:
SVG صورة، يبقى آمن.
مش بالضرورة.
SVG عبارة عن XML-based document، ويمكن أن يحتوي على محتوى نشط أو references.
ولو اتعرض بطريقة غير آمنة ممكن يدخل في سيناريوهات XSS.
لذلك لو مش محتاج SVG:
Reject SVG
ولو محتاجه:
Strict sanitization
+
Safe serving
+
Correct Content-Type
+
Content-Disposition policy
OWASP تحذر من أنواع ملفات يمكن أن تحتوي على HTML/active content، وتوصي بحظر الأنواع غير المطلوبة وتطبيق sanitization عند الحاجة. citeturn0search0
📐 Image Dimensions
دي نقطة ناس كتير تنساها.
ممكن المستخدم يرفع:
100 KB
لكن:
12000 × 12000
يعني الملف صغير نسبيًا، لكن فك الصورة ومعالجتها ممكن يستهلك موارد كبيرة.
عشان كده لازم تفكر في:
File Size
+
Pixel Dimensions
+
Processing Cost
مش:
File Size
فقط.
🧹 Image Normalization
في production ممكن تعمل pipeline مثل:
Upload
↓
Validate
↓
Decode
↓
Resize
↓
Convert
↓
Strip unnecessary metadata
↓
Encode
↓
Store
مثلاً:
user-image.jpg
يتحول إلى:
8f72...webp
وده يعطيك:
- predictable format
- controlled dimensions
- smaller files
- consistent delivery
- less reliance on user-provided metadata
🔒 Private Files
لو الملف حساس:
ID
Passport
Invoice
Contract
Private attachment
خليه:
Private Object
والـdownload يكون:
User
↓
Your API
↓
Authorization
↓
Signed URL / controlled download
↓
Object Storage
مش:
Public URL
👤 Access Control
حتى لو عندك:
signed URL
لسه لازم تسأل:
هل المستخدم أصلًا يملك الملف؟
مثلاً:
GET /documents/123
لازم تتحقق من:
Current User
↓
owns document 123?
↓
YES → allow
NO → deny
ماينفعش:
User A
يغير:
/document/123
إلى:
/document/124
ويقرأ ملف User B.
🗑️ Orphan Files
تخيل:
User uploads image
↓
Storage succeeds
↓
Database update fails
دلوقتي عندك:
File exists
DB reference doesn't
ده:
Orphan File
والعكس ممكن يحصل:
DB reference exists
Storage file missing
وده:
Broken Reference
عشان كده محتاج lifecycle strategy:
Upload
↓
Pending
↓
Validated
↓
Stored
↓
DB reference created
↓
Active
ولو العملية فشلت:
Cleanup
وممكن تعمل periodic reconciliation job يراجع:
DB metadata
vs
Object Storage
ويكتشف الملفات اليتيمة.
🧱 Upload Architecture
Architecture بسيطة:
┌───────────────┐
│ Browser │
└───────┬───────┘
│
│ Upload
▼
┌───────────────┐
│ Your API │
└───────┬───────┘
│
Validate / Authorize
│
▼
┌───────────────┐
│ Object Storage│
└───────┬───────┘
│
▼
┌─────┐
│ CDN │
└──┬──┘
│
▼
Users
والـdatabase:
Database
│
└── file metadata
├── id
├── ownerId
├── objectKey
├── contentType
├── size
├── width
├── height
└── status
🚀 Direct-to-Storage Upload
في الأنظمة الكبيرة، مش لازم الصورة تعدي من الـapplication server بالكامل.
بدل:
Browser
↓
Your API
↓
Your Server
↓
Object Storage
ممكن:
Browser
↓
Your API
↓
Signed Upload URL
↓
Object Storage
وبعدين:
Browser
↓
Object Storage
الـAPI تعمل:
Authorize
Validate intended upload
Generate signed upload URL
والـbrowser يرفع مباشرة.
ده يقلل:
Application bandwidth
Application CPU
Application memory pressure
ومفيد جدًا للملفات الكبيرة.
لكن لازم تفضل عندك validation بعد الرفع، لأن الـsigned URL لا يعني إن الملف أصبح trustworthy.
🔄 Processing Pipeline
ممكن تعمل:
Upload Request
↓
Authorize
↓
Generate Upload URL
↓
Client Uploads
↓
Object Created
↓
Queue / Event
↓
Image Worker
↓
Validate
↓
Decode
↓
Resize
↓
Convert
↓
Scan if required
↓
Store final variants
↓
Update DB
مثلاً:
original
thumbnail
medium
large
وده أفضل من إن كل user request يعمل processing ثقيل بشكل synchronous.
🗃️ Database Design
قاعدة البيانات ممكن تحتفظ بالـmetadata فقط:
files
-------------------------
id
owner_id
object_key
original_name
content_type
size_bytes
width
height
status
created_at
deleted_at
لاحظ:
object_key
هو المهم للـstorage.
مش لازم يكون:
original_name
🔐 Security Layers
أفضل تفكير:
Upload
│
▼
Authentication
│
▼
Authorization
│
▼
Size Validation
│
▼
Type Validation
│
▼
Content Validation
│
▼
Safe Filename
│
▼
Malware Scan*
│
▼
Image Processing
│
▼
Object Storage
* حسب نوع الملفات وحساسية النظام.
🧪 Failure Scenarios
1. Storage Upload succeeds, DB fails
Storage ✅
DB ❌
النتيجة:
Orphan File
الحل:
Cleanup / reconciliation
2. DB succeeds, Storage fails
DB ✅
Storage ❌
النتيجة:
Broken Reference
الحل:
State = Pending/Failed
Retry
Reconciliation
3. User uploads 10 GB
Reject early
4. User uploads executable renamed as image
Validate actual content
Reject
5. User uploads malicious SVG
Reject
or
Sanitize + safe serving
6. User requests someone else's private file
Authorization fails
→ 403/404 according to your security policy
7. Image processing library crashes
Worker isolated
Job fails
Retry / quarantine
Main API remains healthy
❌ Common Wrong Answers
❌ "أحفظها في Database وخلاص"
ممكن، لكن لازم تبرر:
Scale
Backup
Restore
I/O
Blob size
❌ "أحفظها في uploads داخل المشروع"
مقبول كبداية.
لكن مع:
Multiple servers
Deployments
Scaling
Disaster recovery
الموضوع يصبح أصعب.
❌ "أستخدم filename بتاع المستخدم"
❌ خطر.
لأنك تدخل في:
Collision
Path Traversal
Untrusted input
❌ "أتحقق من extension فقط"
.jpg
.png
مش دليل كافي إن المحتوى فعلًا صورة.
❌ "لو MIME type image/jpeg يبقى تمام"
الـMIME المرسل من client ممكن يتزور.
❌ "أي صورة أقدر أعملها public"
غلط جدًا لو عندك:
private documents
user data
identity documents
❌ "CDN يحل كل حاجة"
CDN يحسن delivery.
لكنه لا يحل:
Authentication
Authorization
Validation
Storage lifecycle
Upload security
🎯 Interview Answer
لو الـinterviewer سألك:
"المستخدم رفع صورة، هتعمل إيه؟"
إجابة قوية:
"أول قرار عندي هو storage strategy. في نظام صغير ممكن filesystem يكون كفاية، لكن لو عندي multiple application instances أو scale أكبر، هفضل Object Storage زي S3/Blob Storage، والـdatabase هخزن فيها metadata والـobject key فقط. وللتوزيع السريع أقدر أحط CDN أمام الـobject storage.
من ناحية security، مش هثق في filename أو Content-Type اللي جاي من client. هعمل authentication وauthorization، file size limits، validate للـactual file type، وأولد storage key عشوائي بدل اسم المستخدم. وهحمي نفسي من path traversal، وأتعامل بحذر مع SVG وimage-processing libraries، وأحط limits على أبعاد الصور والـprocessing resources.
لو الملف private، مش هخليه public؛ هستخدم authorization وربما signed URLs للتحميل. ولو الـupload والـDB update ممكن يفشلوا بشكل منفصل، هتعامل مع orphan files وbroken references باستخدام states وcleanup/reconciliation jobs. ولو الملفات كبيرة، ممكن أستخدم direct-to-object-storage upload باستخدام signed URLs بدل ما أعدي الملف كله على الـAPI server.
فالموضوع مش مجرد "استقبل صورة واحفظها". ده security + storage + scalability + lifecycle + access control + reliability."
🧠 Mental Model
احفظها بالشكل ده:
UPLOAD
│
▼
Authentication
│
▼
Authorization
│
▼
Size Validation
│
▼
Type Validation
│
▼
Content Validation
│
▼
Generate Safe Key
│
▼
Object Storage
│
┌──────┴──────┐
▼ ▼
Database CDN
Metadata Delivery
│
▼
Lifecycle
│
┌────┴────┐
▼ ▼
Cleanup Retention
📊 Storage Comparison
| الحل | بسيط | Scale | Multi-Server | CDN | مناسب للملفات الكبيرة |
|---|---|---|---|---|---|
| Database Blob | ⚠️ | ⚠️ | ✅ | ⚠️ | ⚠️ |
| Local Filesystem | ✅ | ❌ | ❌ | ⚠️ | ⚠️ |
| Media Server | ⚠️ | ⚠️ | ✅ | ⚠️ | ✅ |
| Object Storage | ✅ | ✅ | ✅ | ✅ | ✅ |
| Object Storage + CDN | ✅ | ⭐⭐⭐ | ✅ | ⭐⭐⭐ | ⭐⭐⭐ |
مفيش اختيار واحد صح لكل مشروع.
🏆 Production Checklist
Storage
- اختيار storage strategy بناءً على scale
- Object Storage عند الحاجة
- CDN للـpublic/high-traffic assets
- Backup / lifecycle policy
Security
- Authentication
- Authorization
- File size limit
- MIME validation
- File signature/content validation
- Safe generated filename
- Path traversal protection
- SVG policy
- Updated image libraries
- Malware scanning عند الحاجة
Processing
- Dimension limits
- Memory/resource limits
- Async processing عند الحاجة
- Thumbnail generation
- Format normalization
Reliability
- Upload status
- Retry strategy
- Orphan cleanup
- DB/storage reconciliation
- Failed upload handling
Privacy
- Public/private classification
- Authorization checks
- Signed URLs عند الحاجة
- Retention policy
- Deletion workflow
📚 Official Sources
OWASP — File Upload Cheat Sheet
https://cheatsheetseries.owasp.org/cheatsheets/File_Upload_Cheat_Sheet.html
مرجع مهم جدًا لأمان رفع الملفات، ويتناول validation، filenames، limits، storage، malware scanning وغيرها.
OWASP — Path Traversal
https://owasp.org/www-community/attacks/Path_Traversal
شرح رسمي لمشكلة استخدام paths غير الآمنة والمدخلات التي تسمح بالخروج من directory المحدد.
Amazon S3 Documentation
https://docs.aws.amazon.com/AmazonS3/latest/userguide/Welcome.html
التوثيق الرسمي لـAmazon S3 وObject Storage.
Amazon S3 Presigned URLs
https://docs.aws.amazon.com/AmazonS3/latest/userguide/using-presigned-url.html
لرفع/تحميل الملفات باستخدام URLs مؤقتة بدون إعطاء العميل credentials مباشرة.
Microsoft Azure Blob Storage
https://learn.microsoft.com/en-us/azure/storage/blobs/storage-blobs-introduction
التوثيق الرسمي لـAzure Blob Storage.
Google Cloud Storage
https://cloud.google.com/storage/docs/introduction
التوثيق الرسمي لـGoogle Cloud Storage.
<div align="center">
🚀 الخلاصة
رفع صورة مش:
Request
↓
Save File
في production هو أقرب إلى:
Upload
↓
Auth
↓
Authorization
↓
Validation
↓
Safe Storage Key
↓
Object Storage
↓
Processing
↓
Database Metadata
↓
CDN / Signed URL
↓
Lifecycle + Cleanup
والـSenior Engineer مش بس يقول:
"هنستخدم S3."
لكن يعرف ليه S3؟ وإمتى filesystem يكفي؟ وإزاي يحمي الـupload؟ وإزاي يتعامل مع الفشل؟ وإزاي يضمن إن الملف يفضل متاح وآمن وقابل للتوسع؟
</div>منشورات مقترحة
مشاريع ذات صلة

منصة تحرير مستندات تتيح التعاون والتعديل التفاعلي مع إمكانية تصدير المستندات وإدارتها.