#Vấn đề cốt lõi
👉 Upload file là một trong những lỗ hổng nguy hiểm nhất trong web app
Nếu làm sai → attacker có thể:
- Upload shell PHP
- Execute code trên server
- Chiếm quyền hệ thống
#Bad Example (Anti-pattern)
move_uploaded_file($file['tmp_name'], "uploads/{$file['name']}");
#Vấn đề
- Không validate
- Dùng tên file user → dễ overwrite / path traversal
- Lưu trong public → có thể execute
#Good Example (Best Practice)
#1. Validate upload error
if ($file['error'] !== UPLOAD_ERR_OK) {
throw new UploadException('Upload failed');
}
#2. Validate size
if ($file['size'] > 5 * 1024 * 1024) {
throw new UploadException('Too large');
}
#3. Validate MIME (quan trọng nhất)
$finfo = new finfo(FILEINFO_MIME_TYPE);
$mime = $finfo->file($file['tmp_name']);
👉 Không dùng $_FILES['type']
#4. Whitelist
$allowed = [
'image/jpeg' => 'jpg',
'image/png' => 'png',
];
#5. Random filename
$name = bin2hex(random_bytes(16)) . '.jpg';
👉 Không dùng tên user upload
#6. Store outside web root
/storage/uploads
👉 Không để trong public/
#7. Serve qua controller
readfile($path);
👉 Có thể check permission
#Giải thích sâu (Senior mindset)
#1. RCE (Remote Code Execution)
Nếu upload .php vào public:
http://example.com/uploads/shell.php
👉 Server execute code
#2. Double extension attack
shell.php.jpg
👉 Bypass extension check
#3. MIME spoofing
Client gửi:
Content-Type: image/jpeg
👉 Fake được → phải dùng finfo
#4. Path traversal
../../shell.php
👉 Dùng basename() để tránh
#5. Storage strategy
- Public: chỉ static safe files
- Private: upload user
👉 Serve qua controller
#Tips & Tricks
#1. Disable PHP execution trong uploads
location /uploads {
deny all;
}
#2. Image re-encode
👉 Load và save lại image → loại bỏ payload
#3. Virus scan
👉 Dùng ClamAV cho file quan trọng
#4. Rate limit upload
👉 Tránh spam / DoS
#5. Logging
👉 Log upload suspicious
#Interview Questions
1. Tại sao upload file nguy hiểm?
Summary:
- Có thể dẫn tới RCE
Deep: Upload file thực thi → server chạy code attacker
2. Tại sao không check extension?
Summary:
- Dễ bypass
Deep: Double extension hoặc rename file
3. Tại sao không dùng MIME từ client?
Summary:
- Fake được
Deep: Client control header
4. Cách an toàn nhất để lưu file?
Summary:
- Random name + private storage
Deep: Không public + serve qua controller
5. Làm sao để tránh RCE?
Summary:
- Validate + không execute
Deep: MIME check, store ngoài public, disable execution
#Kết luận
👉 File upload là CRITICAL security area
Nếu làm sai → mất server
👉 Luôn:
- Validate
- Random filename
- Store private
- Serve qua controller