Rò rỉ thông tin hiếm khi là lỗ hổng độc lập — nó là thứ rút ngắn giai đoạn thăm dò của kẻ tấn công.
Chỗ rò kinh điển ở phản hồi lỗi:
- Stack trace ra production: lộ framework, phiên bản, đường dẫn tuyệt đối, tên bảng/cột trong DB, thậm chí giá trị biến chứa dữ liệu người dùng.
- Thông báo phân biệt được: "email không tồn tại" so với "sai mật khẩu" cho phép liệt kê tài khoản. Dùng chung một câu và một mã lỗi; cả luồng quên mật khẩu và đăng ký cũng phải trả lời như nhau.
- Kênh phụ theo thời gian: nếu tài khoản không tồn tại thì không chạy hash mật khẩu → phản hồi nhanh hơn rõ rệt. Vẫn chạy một phép hash giả để cân bằng.
- Mã trạng thái nói quá nhiều: trả 403 (tồn tại nhưng không có quyền) so với 404 cho phép dò tài nguyên của người khác. Với tài nguyên nhạy cảm nên trả 404 đồng nhất.
- Header X-Powered-By, Server với số phiên bản; sourcemap và file .git, .env, /debug để lộ trên production.
Chỗ rò trong log:
- Log nguyên request body/header → mật khẩu, token, số thẻ, OTP nằm trong log, rồi log lại chảy sang hệ thống bên thứ ba và được giữ hàng năm.
- Ghi query string đầy đủ (token đặt lại mật khẩu thường nằm ở đây).
- Log PII vượt phạm vi cho phép chia sẻ với nhà cung cấp giám sát.
Thiết kế đúng:
// respond generically, log with detail, correlate by id
catch (err) {
const id = crypto.randomUUID()
logger.error({ id, err, userId: req.user?.id }) // internal only
res.status(500).json({ message: 'Something went wrong', ref: id })
}- Chi tiết vào log nội bộ, phản hồi ra ngoài chỉ có mã tham chiếu — hỗ trợ vẫn tra được, kẻ tấn công không thấy gì.
- Redact theo allowlist: chỉ log các field được liệt kê rõ, thay vì blacklist các field nhạy cảm (blacklist luôn sót field mới).
- Đặt thời hạn lưu cho log và kiểm soát quyền đọc log như dữ liệu sản xuất.
- Ngược lại, phải log đủ các sự kiện bảo mật (đăng nhập thất bại, đổi quyền, đổi email) để còn điều tra được.