AppSec 6 — Bảo mật API
AppSec 6 — Bảo mật API
Suốt series này ta đã bàn bảo mật ứng dụng qua lăng kính OWASP Top 10 (Web). Nhưng thế giới hiện đại không còn xoay quanh trang web nữa — nó xoay quanh API. Mobile app gọi API. Microservice gọi nhau qua API. Đối tác Open Banking gọi API. Data pipeline hút dữ liệu qua API. Với một hệ thống dữ liệu ngân hàng, phần lớn dữ liệu nhạy cảm — số dư, giao dịch, PII khách hàng — đều đi ra ngoài qua một endpoint API nào đó. API chính là bề mặt tấn công lớn nhất và trực tiếp nhất.
Vì sao API cần một danh sách rủi ro riêng thay vì tái dùng Web Top 10? Vì API khác web về bản chất phơi bày:
- Không có UI che chắn. Với web, logic nghiệp vụ ẩn sau giao diện; kẻ tấn công phải suy ra flow. Với API, hợp đồng (contract) được phơi bày trần trụi — endpoint, tham số, cấu trúc dữ liệu — thường kèm cả tài liệu OpenAPI/Swagger. Kẻ tấn công đọc thẳng "menu" của hệ thống.
- Tự động hoá tấn công cực dễ. API sinh ra để máy gọi máy. Không có CAPTCHA, không có nhịp click của con người. Một vòng lặp
forquét 1 triệu ID trong vài phút là chuyện thường — chính điều làm IDOR/BOLA nguy hiểm gấp bội trên API. - Phơi bày logic và dữ liệu trực tiếp. API trả JSON thô, thường lộ nhiều field hơn UI hiển thị, và phản ánh sát cấu trúc dữ liệu bên trong. Một endpoint "vô hại" có thể trả cả
internal_risk_score,is_admin,kyc_statusmà front-end chẳng bao giờ render.
Vì những khác biệt này, OWASP xuất bản riêng API Security Top 10, bản mới nhất là 2023. Bài này đi qua cả 10 mục, rồi tổng hợp biện pháp phòng thủ và đặc thù từng kiểu API.
OWASP API Security Top 10 (2023)
Điểm chung xuyên suốt danh sách 2023: authorization (phân quyền) là chủ đề áp đảo — ba trong mười mục (API1, API3, API5) đều là lỗi authz ở các tầng khác nhau. Đây là thông điệp cốt lõi: với API, phân quyền sai chỗ là kẻ giết người thầm lặng.
API1 — Broken Object Level Authorization (BOLA)
Đứng #1 không phải ngẫu nhiên. BOLA (trên web quen gọi là IDOR — Insecure Direct Object Reference) là lỗi khi API cho phép người dùng truy cập đối tượng của người khác vì server quên kiểm tra quyền sở hữu ở mức từng bản ghi.
GET /api/v1/accounts/12345/transactions ← của tôi
GET /api/v1/accounts/12346/transactions ← đổi ID → của người khác
Nếu server chỉ xác thực JWT hợp lệ rồi trả dữ liệu theo ID trong URL, kẻ tấn công quét tuần tự đọc sạch dữ liệu. Ta đã mổ xẻ kỹ ở bài Access Control; ở đây nhấn mạnh: trên API, BOLA dễ khai thác hơn hẳn vì ID phơi bày công khai và việc quét được tự động hoá hoàn toàn. Phòng thủ duy nhất đúng: kiểm tra chủ sở hữu ở tầng truy vấn cho mọi object, lấy danh tính từ token đã verify — không bao giờ tin ID trong request.
API2 — Broken Authentication
Cơ chế xác thực bị hỏng: cho phép brute-force credential/OTP không giới hạn, chấp nhận JWT với chữ ký yếu hoặc alg: none, token không hết hạn, endpoint đăng nhập/đổi mật khẩu không được bảo vệ, hoặc dùng API key như thể là mật khẩu vĩnh viễn. Xác thực là cánh cổng — thủng cổng thì mọi lớp authz phía sau vô nghĩa. Chi tiết cơ chế OAuth2/JWT đúng đã bàn ở bài Data API 5.
API3 — Broken Object Property Level Authorization
Nếu API1 nói về đối tượng thì API3 nói về thuộc tính (property) của đối tượng. Đây là hợp nhất hai lỗi cũ (2019):
- Excessive Data Exposure (lộ field thừa). API trả về nhiều field hơn cần thiết, tin rằng client sẽ tự lọc. Ví dụ endpoint hồ sơ khách trả cả
national_id,credit_score,internal_notesdù màn hình chỉ hiện tên. Kẻ tấn công đọc thẳng JSON, không quan tâm UI. Lọc ở server, chỉ trả field cần. - Mass Assignment (gán thuộc tính thừa). Ngược lại: API nhận nguyên payload rồi gán thẳng vào object/model. Người dùng gửi thêm
"role": "admin"hoặc"balance": 999999vào body của một request tưởng chừng vô hại (đổi tên hiển thị), và server ngoan ngoãn ghi. Phòng thủ: whitelist field được phép ghi, không bao giờ bind cả body vào entity.
API4 — Unrestricted Resource Consumption
Thiếu giới hạn tài nguyên: không rate limit, không quota, không giới hạn kích thước payload / số bản ghi trả về / độ sâu truy vấn. Hậu quả kép: DoS (một client làm sập service) và chi phí (mỗi request gọi tốn CPU, băng thông, hay dịch vụ tính phí như SMS/OTP, gọi API bên thứ ba trả tiền theo lượt). Trên cloud, một endpoint không quota có thể biến thành hoá đơn khổng lồ. Phòng thủ: rate limit theo client/IP/token, quota theo cửa sổ thời gian, giới hạn page size và độ sâu.
API5 — Broken Function Level Authorization
Song sinh với API1, nhưng ở mức chức năng (function) thay vì đối tượng. Người dùng thường gọi được endpoint quản trị vì server không kiểm tra quyền theo vai trò cho từng route:
GET /api/v1/accounts/12345 ← user thường được phép
DELETE /api/v1/admin/accounts/12345 ← đoán ra route admin, gọi thẳng
Đây là privilege escalation dọc ở dạng API: đoán/mò route admin (/admin, /internal, đổi GET thành DELETE) và thực thi thao tác vượt quyền. Phòng thủ: deny by default, mỗi endpoint kiểm tra role/scope rõ ràng, không dựa vào việc "route này không có link công khai".
API6 — Unrestricted Access to Sensitive Business Flows
Mục "mới" và tinh tế nhất: từng request đều hợp lệ, đúng authz, nhưng kẻ tấn công tự động hoá một luồng nghiệp vụ nhạy cảm ở quy mô bất thường để trục lợi. Ví dụ ngân hàng: bot đăng ký hàng loạt tài khoản để farm ưu đãi mở thẻ, hoặc quét toàn bộ mã khuyến mãi, hoặc lạm dụng luồng chuyển tiền/hoàn tiền. Không có "lỗ hổng kỹ thuật" đơn lẻ — vấn đề là flow thiếu chống lạm dụng (device fingerprint, CAPTCHA có chọn lọc, phát hiện hành vi bất thường, giới hạn nghiệp vụ).
API7 — Server Side Request Forgery (SSRF)
API nhận một URL/định danh tài nguyên từ người dùng rồi tự đi gọi (fetch ảnh từ URL, webhook, import từ link). Kẻ tấn công đưa URL trỏ vào nội bộ: http://169.254.169.254/... (metadata endpoint cloud, lộ credential), http://localhost:6379 (Redis nội bộ), hay các service không phơi ra ngoài. Server trở thành "proxy" tấn công chính hạ tầng của nó. Phòng thủ: whitelist domain/IP đích, chặn dải IP nội bộ/link-local, không trả nguyên response về client.
API8 — Security Misconfiguration
Cấu hình sai/thiếu: CORS mở toang (Access-Control-Allow-Origin: * cho API nhạy cảm), thiếu security header, để bật debug/stack trace lộ thông tin, TLS cấu hình yếu, endpoint mặc định/tài liệu Swagger phơi ra production, cloud storage/bucket để public. Đây thường là lỗ hổng "nhặt được" đầu tiên khi scan.
API9 — Improper Inventory Management
Bạn không thể bảo vệ thứ bạn không biết mình có. Mục này về quản lý danh mục API kém:
- Shadow API — API do team dựng ngoài quy trình, security không biết tới.
- Zombie/deprecated API — phiên bản cũ (
/api/v1/) tưởng đã tắt nhưng vẫn chạy, thiếu bản vá, giữ lỗ hổng cũ khi/api/v2/đã vá. - Undocumented endpoint — endpoint test/nội bộ vô tình phơi ra internet.
Rất nhiều vụ rò rỉ đến từ một API cũ ai cũng quên. Phòng thủ: inventory đầy đủ + versioning kỷ luật — biết mọi endpoint đang sống, retire dứt điểm bản cũ.
API10 — Unsafe Consumption of APIs
Mục hướng vào chính mình khi ta là client gọi API bên thứ ba. Ta thường tin dữ liệu từ đối tác/nhà cung cấp hơn tin input từ người dùng — và đó là sai lầm. Response từ API bên ngoài cũng phải được validate, không đi theo redirect mù quáng, đặt timeout, xử lý lỗi an toàn. Với ngân hàng: dữ liệu từ credit bureau, cổng thanh toán, dịch vụ eKYC đều phải coi là "không đáng tin cho tới khi kiểm chứng".
Biện pháp phòng thủ cốt lõi
Danh sách 10 mục quy về một tập nguyên tắc thực thi. Nhiều điểm đã gặp ở bài Data API 5 và Access Control — ở đây tổng hợp lại dưới góc nhìn API:
| Biện pháp | Chống mục nào |
|---|---|
| Authz ở MỌI tầng — cả object-level (từng bản ghi) lẫn function-level (từng route), kiểm ở server | API1, API5 |
Xác thực mạnh — OAuth2/JWT đúng chuẩn: verify chữ ký, kiểm exp, không dùng alg:none | API2 |
| Rate limiting & quota — theo client/token, giới hạn payload & page size | API4, API6 |
| Validate input & schema — ép kiểu, ràng buộc theo OpenAPI, whitelist field ghi (chống mass assignment) | API3, API8 |
| Chỉ trả field cần — response model tường minh, không "dump" cả entity | API3 |
| API Gateway — điểm chốt chặn tập trung: TLS, auth, rate limit, logging | API2, API4, API8 |
| Quản lý inventory & versioning — biết mọi endpoint, retire bản cũ | API9 |
| HTTPS/TLS mọi nơi — kể cả service-to-service nội bộ | API8 |
| Giám sát & log bất thường — phát hiện quét ID, spike request | API1, API6 |
| Whitelist URL đích, validate response bên thứ ba | API7, API10 |
Điểm cần khắc sâu: API Gateway là chốt chặn nhưng KHÔNG thay thế authz trong service. Gateway lo được auth, rate limit, TLS, logging — những thứ ngang bằng cho mọi request. Nhưng câu hỏi "user này có được xem đúng bản ghi account 12346 không?" chỉ trả lời được ở service, nơi biết dữ liệu và quan hệ sở hữu. Đừng bao giờ giao BOLA cho gateway.
Object-level authorization bằng SQL — minh hoạ
Cách phòng BOLA gọn nhất: authorization filter đi thẳng vào truy vấn — không bao giờ lấy dữ liệu chỉ theo ID từ URL, mà luôn kèm điều kiện chủ sở hữu lấy từ token đã verify. Câu SELECT dưới mô phỏng một endpoint "BOLA-safe": trả về giao dịch của một tài khoản chỉ khi tài khoản đó thuộc đúng khách hàng của phiên đăng nhập (giả lập customer_id = 42), kèm cả thông tin nhân viên phụ trách chi nhánh để cho thấy cách gộp 5 bảng:
-- ▶ Chạy được
SELECT c.full_name, a.account_no, t.amount, t.kind, d.name AS department
FROM transactions t
JOIN accounts a ON a.id = t.account_id
JOIN customers c ON c.id = a.customer_id
JOIN employees e ON e.id = (a.customer_id % 5) + 1
JOIN departments d ON d.id = e.department_id
WHERE a.customer_id = 42
AND a.account_no = 'ACC-0042';
Ý tưởng cốt lõi nằm ở mệnh đề WHERE a.customer_id = 42: nếu kẻ tấn công đổi account_no sang tài khoản người khác, điều kiện chủ sở hữu khiến truy vấn trả về 0 dòng — server không có gì để lộ. Bỏ mệnh đề đó đi, chỉ lọc theo account_no thôi, là ta có ngay một lỗ hổng BOLA. Trong ứng dụng thật, 42 là bind parameter lấy từ danh tính đã xác thực, tuyệt đối không lấy từ tham số URL người dùng gửi. (Phần join employees/departments ở đây chỉ để minh hoạ truy vấn đa bảng; trong thực tế thông tin chi nhánh sẽ nối qua bảng ánh xạ riêng.)
Đặc thù theo kiểu API
Nguyên tắc chung áp cho mọi API, nhưng mỗi kiểu có bề mặt tấn công riêng:
- REST — phổ biến nhất, dựa trên tài nguyên và HTTP verb. Rủi ro điển hình: BOLA (ID trên URL), function-level (đoán route admin), over-exposure trong JSON. Dễ đặt gateway và rate limit vì mỗi endpoint là một URL rõ ràng.
- GraphQL — client tự soạn truy vấn, chọn field. Sức mạnh đó là con dao hai lưỡi:
- Query tốn kém / lồng sâu — một truy vấn có thể lồng nhiều tầng quan hệ (
user → accounts → transactions → ...) tạo tải khổng lồ từ một request. Rate limit theo "số request" không đủ; cần giới hạn độ sâu và độ phức tạp (query cost). - Introspection — GraphQL cho phép truy vấn chính schema của nó. Tiện khi dev, nhưng ở production nó phơi toàn bộ bản đồ dữ liệu cho kẻ tấn công. Nên tắt introspection ở production.
- Authz phải áp ở mức resolver/field, không phải mức endpoint (chỉ có một endpoint
/graphql).
- Query tốn kém / lồng sâu — một truy vấn có thể lồng nhiều tầng quan hệ (
- gRPC — dùng HTTP/2 + Protobuf, thường cho giao tiếp service-to-service nội bộ. Nhị phân nên không "đọc bằng mắt" như JSON, nhưng không phải bảo mật — vẫn cần mTLS, authz per-method, validate. Rủi ro hay bị bỏ qua: coi kênh nội bộ là "an toàn mặc định".
Use case thực tế
Bối cảnh. NCB mở một Open Banking API cho phép các fintech đối tác truy vấn số dư và lịch sử giao dịch của khách hàng (đã có consent) qua REST, đặt sau một API Gateway. Trước go-live, đội security chạy đợt review và phát hiện ba vấn đề trùng khớp ba mục trong Top 10.
Vấn đề 1 — BOLA (API1). Endpoint GET /open/v1/accounts/{account_id}/transactions chỉ verify access token của fintech mà không kiểm tra token đó có consent cho đúng account_id. Pentester dùng token hợp lệ của một khách hàng test rồi tăng account_id — đọc được giao dịch của 128 tài khoản trong 6 phút bằng script quét. Khắc phục: mọi truy vấn account kèm điều kiện chủ sở hữu + phạm vi consent lấy từ token đã verify (đúng như câu SQL trên); chuyển ID sang định danh khó đoán như biện pháp bổ trợ, không thay thế.
Vấn đề 2 — Excessive Data Exposure (API3). Response giao dịch trả nguyên entity nội bộ, lộ cả internal_fraud_score và kyc_flag — những field fintech không có quyền thấy. Khắc phục: định nghĩa response model tường minh, chỉ whitelist các field trong hợp đồng Open Banking; thêm test tự động chặn "field lạ" lọt ra.
Vấn đề 3 — Unrestricted Resource Consumption (API4). Không có quota; một đối tác gọi 40.000 request/phút để đồng bộ toàn bộ lịch sử, làm chậm cả cụm. Khắc phục: gateway áp rate limit theo client_id (ví dụ 600 req/phút, burst 100), giới hạn page size 100 bản ghi, trả 429 Too Many Requests kèm Retry-After. Kết quả: một đối tác lỗi không còn kéo sập dịch vụ dùng chung. Sau đợt vá, đội cũng lập API inventory đầy đủ và tắt endpoint /open/v0/ cũ đang chạy âm thầm (API9).
Ghi nhớ
- API là bề mặt tấn công lớn nhất của hệ thống dữ liệu hiện đại: không UI che, dễ tự động hoá tấn công, phơi bày logic/dữ liệu trực tiếp — nên có OWASP API Security Top 10 (2023) riêng.
- Authorization là chủ đề áp đảo: ba trong mười mục (API1 BOLA, API3 property-level, API5 function-level) đều là lỗi authz ở các tầng khác nhau.
- API1 — BOLA/IDOR (#1): quên kiểm tra chủ sở hữu ở mức bản ghi; phòng thủ = lọc theo owner lấy từ token ngay trong truy vấn, không tin ID từ client.
- API3: chống excessive data exposure (chỉ trả field cần) và mass assignment (whitelist field được ghi).
- API4/API6: rate limit, quota, giới hạn payload/độ sâu chống DoS, chi phí và lạm dụng luồng nghiệp vụ.
- API7 SSRF, API8 misconfig, API9 inventory (shadow/zombie API), API10 (không tin response bên thứ ba) — các mục còn lại cần giám sát đều đặn.
- API Gateway là chốt chặn (TLS, auth, rate limit, logging) nhưng KHÔNG thay authz object-level trong service.
- GraphQL cần giới hạn query cost/độ sâu và tắt introspection ở production; gRPC nội bộ vẫn cần mTLS + authz per-method — "nội bộ" không phải là "an toàn".
- Bối cảnh ngân hàng: API dữ liệu khách hàng/giao dịch và Open Banking là nơi consent + object-level authz + quota phải làm cực chặt.
Bài viết liên quan
Mô hình Git, các lệnh cốt lõi, nhánh & merge, workflow nhóm (Git Flow, trunk-based) và Pull Request.
Continuous Integration/Delivery/Deployment, cấu trúc pipeline, ví dụ GitHub Actions và chiến lược release.
Container vs máy ảo, image/layer, Dockerfile, volume, network, Docker Compose và best practices.
Vì sao cần orchestration; Pod, Deployment, Service, Ingress; scaling, self-healing và cấu hình.