Họ đang đo gì
Bạn có hiểu vì sao truy vấn tham số hoá an toàn không, hay chỉ biết “dùng prepared statement”.
Trả lời ngắn~30 giây
Xảy ra khi dữ liệu người dùng được nối thẳng vào chuỗi SQL, nên nó được PHÂN TÍCH như cú pháp thay vì được coi là giá trị. Cách chặn đáng tin duy nhất là truy vấn tham số hoá: câu lệnh được gửi và biên dịch trước, tham số gửi riêng, nên dù nội dung có là '; DROP TABLE users; -- thì nó vẫn chỉ là một chuỗi. Escape thủ công không đáng tin vì quy tắc khác nhau theo engine, theo bảng mã, và theo ngữ cảnh trong câu lệnh.
Giải thích sâu
Chỗ tham số hoá KHÔNG giúp được là tên bảng, tên cột và hướng sắp xếp — chúng là cấu trúc chứ không phải giá trị, nên không truyền làm tham số được. Đây chính là chỗ injection còn sót lại trong các API có ?sort= hoặc ?orderBy=. Cách xử lý duy nhất đúng là danh sách cho phép: ánh xạ giá trị người dùng gửi sang một tập cột đã biết, và từ chối mọi thứ khác. Không phải escape, không phải regex.
ORM giảm rủi ro nhưng không xoá nó: gần như ORM nào cũng có lối thoát để chạy SQL thô, và đó là nơi lỗ hổng xuất hiện. Ngoài ra còn injection bậc hai: dữ liệu độc được lưu an toàn vào database, rồi sau đó bị đọc ra và nối vào một truy vấn khác — lúc đó nguồn dữ liệu là chính database nên không ai nghĩ nó cần kiểm tra.
Lớp phòng thủ thứ hai đáng có là quyền tối thiểu cho tài khoản database của ứng dụng: không cần DROP, không cần đọc bảng của schema khác, không cần superuser. Nó không ngăn injection nhưng giới hạn hậu quả — và trong sự cố thật, khác biệt giữa “rò rỉ một bảng” và “mất toàn bộ database” thường nằm ở dòng GRANT chứ không ở code.
// Injection: dữ liệu trở thành cú pháp
db.query(`SELECT * FROM users WHERE email = '${email}'`);
// An toàn: câu lệnh và dữ liệu đi riêng
db.query('SELECT * FROM users WHERE email = $1', [email]);
// Tên cột KHÔNG tham số hoá được -> danh sách cho phép
const SORTABLE = { name: 'name', created: 'created_at' } as const;
const column = SORTABLE[req.query.sort as keyof typeof SORTABLE] ?? 'created_at';
db.query(`SELECT * FROM users ORDER BY ${column} DESC LIMIT $1`, [limit]);Câu hỏi tiếp theo họ sẽ hỏi
?Blind SQL injection là gì?
Khi ứng dụng không trả về kết quả truy vấn nhưng vẫn để lộ thông tin qua hành vi — phản hồi khác nhau giữa điều kiện đúng và sai, hoặc thời gian phản hồi khi kẻ tấn công chèn pg_sleep. Nghĩa là “không hiển thị lỗi” không phải một biện pháp bảo vệ.
Trả lời thế này là mất điểm
- Chặn bằng cách lọc từ khoá như
DROPhay--. Danh sách chặn luôn có lỗ, và nó chặn nhầm cả dữ liệu hợp lệ.