Mở đầu
Trong các ứng dụng web hiện đại, khả năng cho phép người dùng upload file là một tính năng phổ biến. Tuy nhiên, nếu không được xử lý đúng cách, nó có thể trở thành lỗ hổng bảo mật nghiêm trọng. Bài viết này sẽ hướng dẫn cách xây dựng một hệ thống upload file an toàn bằng Node.js và thư viện Multer, đồng thời đề cập đến các thực hành bảo mật quan trọng.
Vì sao cần quan tâm đến bảo mật khi upload file?
File upload là một trong những cửa ngõ dễ bị khai thác nhất. Kẻ tấn công có thể upload file chứa mã độc, script ẩn, hoặc file giả mạo định dạng nhằm chiếm quyền kiểm soát server. Nếu không kiểm tra kỹ lưỡng, hệ thống có thể bị xâm nhập, dữ liệu bị đánh cắp, hoặc server bị treo do upload file quá lớn. Do đó, việc thiết kế hệ thống upload an toàn là điều bắt buộc, không phải là tùy chọn.
Multer là gì và tại sao nên dùng nó?
Multer là middleware phổ biến nhất cho Node.js, được thiết kế riêng để xử lý multipart/form-data – định dạng dữ liệu thường dùng khi upload file. Multer giúp dễ dàng lưu file vào disk hoặc memory, đồng thời cung cấp các tùy chỉnh về giới hạn kích thước, filter file type, và đặt tên file. Tuy nhiên, Multer chỉ xử lý việc lưu file, còn việc kiểm tra bảo mật phải do developer tự thực hiện.
Các bước xây dựng hệ thống upload an toàn
1. Thiết lập dự án và cài đặt dependencies
Trước tiên, khởi tạo một project Node.js và cài đặt các thư viện cần thiết:
npm init -y
npm install express multer fs-extra2. Cấu hình Multer với các tùy chọn bảo mật
Tạo thư mục uploads để lưu file và cấu hình Multer:
const express = require('express');
const multer = require('multer');
const path = require('path');
const fs = require('fs');
const app = express();
// Tạo thư mục uploads nếu chưa có
const uploadDir = 'uploads';
if (!fs.existsSync(uploadDir)) {
fs.mkdirSync(uploadDir);
}
// Cấu hình Multer
const storage = multer.diskStorage({
destination: (req, file, cb) => {
cb(null, uploadDir);
},
filename: (req, file, cb) => {
// Đặt tên file an toàn, tránh overwrite
const uniqueSuffix = Date.now() + '-' + Math.round(Math.random() * 1E9);
cb(null, uniqueSuffix + path.extname(file.originalname));
}
});
// Filter file type
function fileFilter(req, file, cb) {
const allowedTypes = /jpeg|jpg|png|gif|pdf/;
const extname = allowedTypes.test(path.extname(file.originalname).toLowerCase());
const mimetype = allowedTypes.test(file.mimetype);
if (extname && mimetype) {
return cb(null, true);
} else {
cb(new Error('Chỉ cho phép upload file ảnh và PDF'));
}
}
// Giới hạn kích thước file
const limits = {
fileSize: 1024 1024 5 // 5MB
};
const upload = multer({
storage: storage,
fileFilter: fileFilter,
limits: limits
});