Authentication và authorization: QA nên kiểm quyền truy cập thế nào
Nhiều lỗi bảo mật nghiêm trọng bắt đầu từ nhầm lẫn đơn giản: hệ thống biết bạn là ai, nhưng quên kiểm object có thuộc về bạn không.
- Không token: 401
- Token sai: 401
- Đúng user, sai role: 403 hoặc không có action
- Đúng role, sai object: 403 hoặc 404
Authentication trả lời “bạn là ai?”
Authentication là login, password, token, session và expiry. Nó chứng minh danh tính nhưng không quyết định bạn được xem gì.
Hãy kiểm thông tin đúng, password sai, email viết hoa, thiếu token, token hết hạn và logout.
Authorization trả lời “bạn được làm gì?”
Authorization kiểm role và quyền sở hữu object. User đã login vẫn không được mở đơn hàng hay tài khoản của người khác.
API hay lỗi ở đây vì id dễ đoán, còn nút ẩn trên frontend không bảo vệ backend.
Kiểm quyền sở hữu object
Dùng hai user. Mở object bằng owner, ghi id, rồi gọi id đó bằng token user thứ hai. Thường expected là 403 hoặc 404.
Vì sao đôi khi 404 tốt hơn
Với object riêng tư, 404 che luôn việc object có tồn tại hay không. 403 cho biết nó tồn tại nhưng bạn không được xem.
Kiểm role
Role phải kiểm cả UI lẫn API. Nếu nút admin bị ẩn, vẫn gửi request bằng token user thường.
Nút ẩn là tiện ích. Request bị từ chối mới là bảo mật.
Access bug thường gặp
Thiếu filter owner, chỉ kiểm role ở frontend, token vẫn dùng được sau logout, endpoint lộ khác biệt giữa object riêng tư tồn tại và không tồn tại.
Bug report về access cần hai account, ngữ cảnh token và object id.
Matrix actor × action × object
Danh sách role là chưa đủ. Tạo row như manager A đọc client A, manager A sửa client B, user thường gọi admin endpoint, nhân viên cũ dùng token cũ. Mỗi row cần expected allow, deny hoặc hide.
Đừng chỉ test GET. BOLA có thể đã chặn read nhưng còn trong PATCH, DELETE, bulk endpoint, export hoặc id lồng trong body. UUID khó đoán hơn nhưng không thay permission check phía server.
Actor Action Object Expected\nmanager A GET client A 200\nmanager A GET client B 403/404 theo contract\nmanager A PATCH client B 403/404, data không đổi\nviewer DELETE client A 403\nno token GET client A 401 + WWW-Authenticate
Vòng đời session và token
Kiểm issue, rotation sau login hoặc đổi quyền, expiry, refresh, logout, revoke, đổi password và nhiều thiết bị. Một số architecture cho access token cũ sống tới expiry ngắn; khi đó logout phải revoke refresh và UI không được hứa hơn server.
- Session id đổi sau login để ngăn session fixation.
- Cookie có Secure, HttpOnly, SameSite phù hợp; token không lộ trong URL hoặc log.
- Token hết hạn, hỏng hoặc ký sai key bị từ chối.
- Thay đổi role đến permission check trong thời gian đã thống nhất dù có cache.
- Refresh reuse, logout và đổi password theo policy đã document.
BOLA, BFLA và quyền trên field
BOLA là user được gọi function nhưng backend không kiểm object id cụ thể. BFLA là role không được gọi function, ví dụ manager gọi admin export. Lỗi property-level làm lộ hoặc nhận field mà role không được dùng.
Trong CRM, hãy test client id của người khác, admin-only bulk export và PATCH có owner_id hoặc is_vip mà form không hiển thị. Ẩn button không xử lý các risk phía server này.
Ví dụ thực tế: ba lỗi phân quyền khác nhau trong CRM
Một lần kiểm client của người khác chưa chứng minh hệ thống an toàn. Dùng hai manager cùng tenant, một admin và một viewer chỉ đọc. Tạo client giả có owner rõ ràng. Sau đó kiểm ba ranh giới độc lập: object, function và field.
- Xác định role và owner của object.
- Gọi id object của người khác.
- Gọi thẳng function chỉ dành cho admin.
- Thử ghi một property bị cấm.
BOLA: function được dùng, object của người khác
Manager B được gọi GET /clients/{id} với client của mình. Dùng token B gửi id client A và mong đợi 403 hoặc 404 theo contract, không lộ dữ liệu A. Lặp lại với PATCH và DELETE trong môi trường test, cả id nằm trong body. UUID khó đoán không phải kiểm quyền: id có thể lọt qua export hoặc link.
BFLA: cả function bị cấm
Export hàng loạt hoặc đổi owner có thể chỉ dành cho admin. Ẩn button không chứng minh gì; gọi trực tiếp admin endpoint với token B. Kiểm request bị từ chối và không có tác dụng phụ: không tạo file export, không đổi owner hay đẩy event vào queue. So với lời gọi thành công của admin để tránh nhầm endpoint hỏng với authorization đúng.
Property-level: function hợp lệ, field bị cấm
Manager có thể sửa số điện thoại client của mình nhưng không được sửa `owner_id` hay `is_vip`. Thêm các field này vào PATCH bằng Postman dù UI không hiển thị. Đọc lại object với quyền admin để chắc field bị cấm không đổi. Response 200 âm thầm bỏ qua chỉ đúng nếu contract quy định. Kiểm cả việc viewer có đọc được field nhạy cảm không.
Thêm tenant và vòng đời quyền
Trong CRM nhiều tenant, hai user cùng role ở hai tổ chức khác nhau cần case cross-tenant riêng. Sau khi thu hồi role hay chuyển owner, thử lại token cũ và đo độ trễ cache theo tài liệu. Logout, 401 so với 403 và cách che object bằng 404 phải theo chính sách sản phẩm, không theo sở thích tester.
Actor Request Expected\nmanager B GET /clients/A deny; no A data\nmanager B PATCH /clients/A deny; no write\nmanager B POST /admin/clients/export deny; no file\nmanager A PATCH /clients/A {is_vip:true} deny/ignore per contract\nviewer GET /clients/A only safe fields\nno token GET /clients/A 401
Bằng chứng cần có gì?
Với mỗi request bị chặn, lưu role, id, method, status, response đã che PII và bằng chứng không có tác dụng phụ. Report riêng ba loại lỗi vì nguyên nhân và cách sửa khác nhau. Nếu truy cập được dữ liệu thật của người khác, escalate qua kênh bảo mật riêng.
Tình huống thứ hai: quyền sau khi đổi manager phụ trách
CRM chuyển client từ manager A sang B. Cả hai đã đăng nhập và permission được cache. B phải có quyền trong khoảng thời gian đã thống nhất, còn A phải mất quyền. Đăng nhập lại không đủ để kiểm: token và link cũ của A có thể vẫn dùng được.
Định nghĩa chính sách thời gian
Hỏi thu hồi quyền tức thì hay cho phép độ trễ cache ngắn. “Đến lần đăng nhập sau” có thể không phù hợp với dữ liệu nhạy cảm, nhưng QA không tự bịa TTL. Ghi thời gian tối đa và nguồn sự thật: owner_id, permission service hay claim trong token. Chuẩn bị hai session đang hoạt động và đồng hồ có thể kiểm soát.
Kiểm quyền trước và sau
Trước khi chuyển: A đọc/sửa, B bị từ chối; sau thời hạn: ngược lại. Kiểm GET, PATCH, danh sách client, export và task liên kết vì endpoint có thể filter khác nhau. Gọi API trực tiếp với token cũ của A quan trọng hơn việc card biến mất khỏi UI. Request bị từ chối không được ghi dữ liệu một phần.
Thử race khi chuyển quyền
PATCH của A chạy đồng thời với reassignment cần quy tắc thứ tự: nó có thể bắt đầu trước khi owner_id đổi nhưng commit sau. Nếu contract chưa xác định điểm quyết định, hãy nêu câu hỏi. Lưu timestamp, phiên bản object và correlation id của hai request. Kết quả thứ tự khác nhau trong một lần chạy chưa chắc là defect.
Kiểm field và lịch sử
A có thể mất quyền trên card hiện tại nhưng vẫn tìm thấy dữ liệu trong activity, search hoặc export. B thấy card nhưng không thấy deal hay task vì owner_id trên object liên kết vẫn cũ. Liệt kê resource và expected access; đôi khi manager cũ hợp lệ giữ quyền xem audit, đôi khi không. Làm rõ rule thay vì đoán.
Đo mà không làm lộ dữ liệu
Dùng account và client giả. Ghi role, tuổi token, thời điểm chuyển quyền, response và việc không có write sau mỗi bước. Không đính kèm card khách hàng thật vào issue công khai. Nếu quyền cũ còn hiệu lực, báo chính xác thời gian, method và object thay vì “cache đôi khi bị cũ”.
Time A GET/PATCH B GET/PATCH\nbefore reassignment allow deny\nafter agreed TTL deny allow\nold token A deny —\nsearch/export A no client A —\nlinked tasks per ownership policy
Bằng chứng đủ là gì?
Không chỉ owner_id đổi trong storage, mà hành vi nhất quán trên đường đọc và ghi sau mốc thời gian đã thống nhất. Token cũ còn dùng quá lâu là phát hiện bảo mật riêng, kèm thời lượng đo được trên dữ liệu test.
Câu hỏi thường gặp về access
Object riêng tư của user khác nên trả 403 hay 404?
Cả hai có thể đúng theo contract. 404 giấu việc object tồn tại, 403 nói rõ bị từ chối. Điều bắt buộc là nhất quán, không lộ data và không thực hiện action.
UUID có ngăn BOLA không?
Không. UUID giảm khả năng đoán nhưng id có thể lộ qua link, log, export hoặc response. Server vẫn phải kiểm authorization cho từng object.
Kiểm role qua UI đã đủ chưa?
Chưa. Control bị ẩn chỉ cải thiện UX; request vẫn gửi trực tiếp được. Server phải authorize mọi method và object.
Kiểm access ít bước nhưng tác động lớn: đôi khi chỉ đổi một id là thấy backend có bảo vệ dữ liệu thật hay không.