File upload là bề mặt tấn công lớn — đừng tin bất cứ gì client gửi.
- Giới hạn size: chặn từ presigned URL / cấu hình server để tránh DoS đầy đĩa.
- Không tin extension và
Content-Type: cả hai do client đặt và giả mạo được (một file.jpgcó thể là script). Kiểm tra bằng magic bytes (nội dung thật) và allowlist loại được phép, không dùng blocklist. - Đổi tên khi lưu: sinh tên ngẫu nhiên (UUID) + set extension theo type đã xác minh; tránh path traversal (
../) và đè file. - Lưu ngoài web root / trên object storage riêng: phục vụ qua route có kiểm soát, không cho thực thi file trong thư mục upload.
- Đặt
Content-Typeđúng khi trả +Content-Disposition: attachmentcho file tải xuống; chống trình duyệt tự đoán kiểu (X-Content-Type-Options: nosniff) để tránh content-type sniffing biến ảnh thành HTML/JS chạy được. - Quét virus/malware (vd ClamAV) bất đồng bộ trước khi cho dùng; với ảnh có thể re-encode để bỏ payload nhúng.
js
// allowlist by real content (magic bytes), never by extension/Content-Type
const MAGIC = { ffd8ff: "image/jpeg", "89504e47": "image/png" }
const head = buffer.subarray(0, 4).toString("hex")
const type = Object.entries(MAGIC).find(([sig]) => head.startsWith(sig))?.[1]
if (!type) throw new Error("file type not allowed")Bẫy kinh điển: chỉ chặn theo extension, tin Content-Type client gửi, hoặc phục vụ file từ thư mục cho phép thực thi (dẫn tới RCE).