Săn lùng BOLA: Cách kiểm tra bảo mật API với OWASP ZAP

Security tutorial - IT technology blog
Security tutorial - IT technology blog

Mối đe dọa vô hình trong API của bạn

Bạn đã dành cả một sprint 40 giờ để thắt chặt luồng OAuth2 và xoay vòng (rotate) các khóa JWT. Bạn cảm thấy an toàn. Nhưng với nhiều API, cửa trước có thể đã khóa chặt nhưng các cửa sổ phía sau lại đang mở toang. Mối đe dọa lớn nhất thường không phải là một hacker đột nhập vào hệ thống — mà là một người dùng hợp lệ truy cập vào dữ liệu không thuộc về họ. Đây chính là Broken Object Level Authorization (BOLA) (Lỗi phân quyền cấp đối tượng), và nó đã giữ vị trí số 1 trong danh sách OWASP API Security Top 10 suốt nhiều năm qua.

Bảo mật không phải là một tính năng “có thì tốt” được gắn thêm vào lúc cuối. Sau khi chứng kiến một botnet tấn công dồn dập vào máy chủ của mình bằng các nỗ lực brute-force SSH lúc 3 giờ sáng, tôi đã ngừng xem bảo mật là một nhiệm vụ của “Giai đoạn 2”. Trải nghiệm đó đã chứng minh rằng hacker luôn tìm kiếm con đường ít bị kháng cự nhất. Trong các ứng dụng hiện đại, con đường đó thường là một endpoint API được bảo vệ kém, tin tưởng vào bất kỳ user_id hay order_id nào mà bạn gửi lên.

Chạy ZAP trong chưa đầy 5 phút

OWASP ZAP (Zed Attack Proxy) là công cụ tiêu chuẩn trong ngành để tìm kiếm những lỗ hổng này. Không giống như các trình quét web kiểu cũ chỉ thu thập dữ liệu (crawl) các liên kết HTML, ZAP phân tích các định nghĩa API để ánh xạ các góc khuất trong ứng dụng của bạn.

1. Tải bộ cài đặt

Tải xuống ZAP cho hệ điều hành của bạn. Sau khi khởi chạy, hãy kiểm tra Marketplace (biểu tượng hộp màu sắc) để đảm bảo add-on OpenAPI Support đã được kích hoạt. Bạn sẽ cần nó để giao tiếp với các API hiện đại.

2. Tăng tốc với định nghĩa API

Nếu API của bạn có tài liệu Swagger — thường nằm tại /swagger.json hoặc /api-docs — việc nhập nó vào là cách nhanh nhất để bắt đầu. Nó luôn hiệu quả hơn việc tìm dò thủ công.

  • Truy cập Import > Import an OpenAPI definition from a URL.
  • Dán URL tài liệu của bạn vào.
  • Quan sát ZAP tự động điền vào cây “Sites” mọi endpoint hiện có.

3. Bắt đầu với một đợt quét cơ bản (Baseline Scan)

Nhấp chuột phải vào host API trong tab “Sites”, sau đó chọn Attack > Active Scan. Đây là đợt quét tổng quát để tìm SQL injection và XSS. Tuy nhiên, nó thường bỏ sót BOLA vì BOLA yêu cầu hiểu biết về việc ai sở hữu dữ liệu nào. Chúng ta cần đi sâu hơn.

Giải phẫu một cuộc tấn công BOLA

BOLA xảy ra khi máy chủ tin tưởng vào ID trong URL mà không kiểm tra chủ sở hữu phiên làm việc. Nó tương đương với việc một thẻ khóa khách sạn có thể mở được mọi căn phòng trên cùng một tầng.

Hãy tưởng tượng một endpoint để lấy chi tiết hóa đơn:

GET /api/v1/invoices/9901

Nếu tôi đăng nhập với tư cách là Người dùng A, tôi sẽ thấy hóa đơn 9901. Nhưng chuyện gì sẽ xảy ra nếu tôi đổi con số đó thành 9902? Nếu máy chủ chỉ kiểm tra xem tôi đã đăng nhập chưa (Authentication – Xác thực) nhưng không kiểm tra xem hóa đơn 9902 có thực sự là của tôi không (Authorization – Phân quyền), bạn đang gặp phải một vụ rò rỉ dữ liệu.

Tại sao các trình quét truyền thống lại thất bại

Các công cụ tự động rất thích các khuôn mẫu (pattern). Chúng có thể nhận diện lỗi 500 hoặc rò rỉ cơ sở dữ liệu ngay lập tức. Nhưng một cuộc khai thác BOLA trông giống như một yêu cầu hoàn toàn hợp lệ. Máy chủ trả về 200 OK với JSON hợp lệ. Đối với một công cụ đơn giản, điều đó trông có vẻ thành công. Nhưng đối với một doanh nghiệp, đó là sự rò rỉ các hồ sơ khách hàng nhạy cảm.

Fuzzing tìm BOLA với ZAP

Để tìm BOLA, chúng ta mô phỏng một kẻ tấn công dò tìm các ID một cách hệ thống. Fuzzer của ZAP là công cụ cho công việc này.

Bước 1: Thu thập yêu cầu (Capture the Request)

Tìm một yêu cầu trong tab “History” của ZAP có chứa một ID, ví dụ như /api/users/me/profile hoặc /api/orders/101.

Bước 2: Thiết lập Fuzz

  1. Nhấp chuột phải vào yêu cầu > Fuzz…
  2. Bôi đen ID dạng số (ví dụ: 101) và nhấn Add…
  3. Chọn Series trong cửa sổ Payloads để tạo ra một dải số từ 100 đến 1.000.
  4. Nhấn Start Fuzzer.

Bước 3: Nhận diện các điểm bất thường

Hãy chú ý đến cột Size Resp. Body trong tab Fuzzer. Nếu 99% các yêu cầu của bạn trả về thông báo “Forbidden” (Bị cấm) dài 400 byte nhưng có một cái trả về 12KB dữ liệu, bạn đã tìm thấy bằng chứng đanh thép. Phản hồi 12KB đó rất có khả năng là dữ liệu thuộc về một người dùng khác.

Mẹo chuyên nghiệp: Kiểm thử kiểm soát truy cập (Access Control Testing)

Để kiểm tra chuyên nghiệp, hãy sử dụng add-on Access Control Testing. Bạn có thể định nghĩa hai người dùng (Người dùng A và Người dùng B), ghi lại một phiên làm việc dưới dạng Người dùng A, và yêu cầu ZAP phát lại phiên đó bằng token của Người dùng B. Nếu Người dùng B lấy được dữ liệu riêng tư của Người dùng A thành công, công cụ sẽ tự động đánh dấu lỗi.

# Kiểm thử BOLA thủ công bằng cURL
# Token của Người dùng A cố gắng lấy bản ghi của Người dùng B
curl -H "Authorization: Bearer <User_A_Token>" \
     "https://api.example.com/v1/private-data/user-B-id"

Khắc phục nguyên nhân gốc rễ

Tìm thấy lỗ hổng là một chiến thắng lớn, nhưng nhiệm vụ thực sự của bạn là khắc phục nó vĩnh viễn. Điều này đòi hỏi bạn phải thay đổi cách tư duy về quyền truy cập dữ liệu.

1. Ngừng sử dụng ID từ phía Client

Nếu người dùng muốn xem hồ sơ của chính họ, đừng dựa vào /api/users/123. Hãy sử dụng /api/users/me. Máy chủ của bạn nên trích xuất ID người dùng trực tiếp từ phiên làm việc an toàn hoặc JWT, khiến người dùng không thể dò tìm một ID khác.

2. Thực thi quyền sở hữu trong mọi truy vấn

Mọi truy vấn cơ sở dữ liệu phải bao gồm kiểm tra quyền sở hữu. Đừng chỉ lấy dữ liệu theo ID; hãy lấy theo ID và Chủ sở hữu.

# Bảo mật
def get_order(order_id, current_user_id):
    # Truy vấn này đảm bảo đơn hàng thuộc về người đang yêu cầu
    return db.query("SELECT * FROM orders WHERE id = ? AND user_id = ?", 
                    order_id, current_user_id)

3. Chuyển sang sử dụng UUID

ID tuần tự (1, 2, 3…) giống như một lời mời gọi kẻ tấn công thu thập toàn bộ cơ sở dữ liệu của bạn. Sử dụng các UUID như 550e8400-e29b-41d4-a716-446655440000 không sửa được lỗi logic, nhưng nó khiến việc “dò đoán” ID trở nên gần như không thể.

4. Tự động hóa bảo mật trong Pipeline

Đừng đợi đến đợt kiểm toán thủ công hàng năm. Hãy chạy OWASP ZAP trong một container Docker ở chế độ headless như một phần của GitHub Actions. Điều này đảm bảo rằng mọi pull request đều được kiểm tra các lỗi API cơ bản trước khi bất kỳ dòng code nào được đưa lên môi trường production.

# Chạy quét ZAP API trong pipeline CI/CD
docker run -v $(pwd):/zap/wrk/:rw -t owasp/zap2docker-stable zap-api-scan.py \
    -t https://api.example.com/swagger.json -f openapi -r report.html

Xây dựng các API bảo mật là một quá trình lặp đi lặp lại. Bằng cách kết hợp khả năng tự động hóa của ZAP với tư duy “xác thực mọi thứ”, bạn có thể giữ cho dữ liệu của người dùng an toàn trước những kiểu khai thác API nguy hiểm nhất hiện nay.

Share: