IDOR / BOLA (Broken Object Level Authorization) xảy ra khi endpoint nhận định danh đối tượng từ client rồi trả về dữ liệu mà không kiểm tra người gọi có quyền trên chính đối tượng đó. Đổi GET /api/invoices/1042 thành 1043 là đọc được hoá đơn người khác. Đây là hạng mục số 1 của OWASP API Security Top 10 vì nó xuất hiện ở mọi endpoint có tham số id.
Đổi id tuần tự sang UUID chỉ làm khó việc đoán, không phải bản vá — id vẫn lộ qua chia sẻ link, log, response của endpoint khác.
Cách chặn ở mức hệ thống, không dựa vào trí nhớ:
1. Ràng buộc chủ sở hữu ngay trong truy vấn, không truy vấn rồi mới so sánh. Đây là biện pháp hiệu quả nhất vì quên là ra 0 dòng, không phải rò dữ liệu.
-- Instead of: SELECT * FROM invoices WHERE id = $1
SELECT * FROM invoices WHERE id = $1 AND org_id = $2; -- org_id from session2. Ép qua một lớp truy cập dữ liệu duy nhất nhận actor làm tham số bắt buộc — không repository nào cho phép gọi mà thiếu ngữ cảnh người dùng. Ở Postgres có thể siết bằng row-level security với biến phiên.
3. Trả 404 thay vì 403 cho đối tượng không thuộc quyền, để không xác nhận rằng id đó tồn tại.
4. Kiểm thử tự động: với mỗi endpoint có id, chạy một test "user A gọi tài nguyên của user B" và bắt buộc kết quả là từ chối. Đây là dạng lỗi mà quét tự động khó phát hiện nhưng test hai-tài-khoản thì bắt được ngay.
5. Đừng quên các đường vòng: endpoint hàng loạt (?ids=1,2,3), bộ lọc ?userId=, GraphQL đi qua quan hệ lồng nhau, endpoint xuất báo cáo, và webhook.