Họ đang đo gì
Bạn có nghĩ về phân quyền như một tính chất kiến trúc không, hay như một câu if trong controller.
Trả lời ngắn~30 giây
Broken access control (A01 trong OWASP Top 10 2021). Dạng phổ biến nhất là IDOR: /api/orders/1234 chỉ kiểm tra người dùng đã đăng nhập chứ không kiểm tra đơn đó có phải của họ. Chỗ phòng đúng không phải trong từng controller mà ở tầng truy cập dữ liệu: mọi truy vấn đều mang theo phạm vi của người gọi. Kiểm tra rải rác trong controller sẽ đúng ở 40 endpoint đầu và thiếu ở endpoint thứ 41 — endpoint mà ai đó thêm vào lúc gấp.
Giải thích sâu
Cách làm bền vững nhất là khiến việc truy vấn KHÔNG có phạm vi trở nên khó viết. Ví dụ mọi hàm truy cập nhận actor làm tham số bắt buộc và tự thêm điều kiện WHERE owner_id = actor.id; hoặc dùng row-level security ở database, để cả những đường đi mà bạn quên vẫn bị chặn. Điểm chung là phòng thủ nằm ở nơi không thể bỏ qua, chứ không phải nơi cần nhớ.
Một dạng khác hay bị bỏ sót là phân quyền theo THAO TÁC chứ không theo tài nguyên: người dùng xem được đơn hàng của mình là đúng, nhưng họ có được HUỶ nó sau khi đã giao không? Có được đổi trường status qua API cập nhật chung không? Rất nhiều lỗ hổng nằm ở chỗ endpoint cập nhật nhận cả object và ghi đè những trường mà người dùng lẽ ra không được chạm — mass assignment. Cách chặn là danh sách trường cho phép, không phải danh sách trường cấm.
Về kiểm tra, đây là loại lỗ hổng mà quét tự động gần như vô dụng: công cụ không biết đơn hàng 1234 thuộc về ai. Nên nó phải được kiểm bằng test có chủ đích — với mỗi endpoint, một test dùng danh tính của người dùng B để truy cập tài nguyên của người dùng A và mong đợi 404. Viết một helper cho việc đó rồi áp cho mọi endpoint là cách rẻ nhất để biến một loại lỗ hổng thành một checklist.
Câu hỏi tiếp theo họ sẽ hỏi
?Trả 404 hay 403 cho tài nguyên không thuộc về người dùng?
404, để không tiết lộ tài nguyên đó tồn tại — nếu không, kẻ tấn công quét id và biết được bạn có bao nhiêu đơn hàng. Nhưng log phải phân biệt hai trường hợp, nếu không đội hỗ trợ không chẩn đoán được gì.
Trả lời thế này là mất điểm
- Kiểm tra quyền ở phía giao diện. Ẩn nút không ngăn được ai gọi thẳng API.
- Tin vào một id do client gửi để xác định người dùng.
?userId=là lỗ hổng, danh tính phải lấy từ phiên.