© 2026 Laravel

File Upload Security – Tránh RCE khi upload file

3 phút đọc 28 lượt xem

#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