Tư duy Feynman • Trực quan hình họa • Java / Spring Boot

Chương 23: OWASP API Security Top 10 (2023)

Bảo vệ toàn diện hệ sinh thái API hiện đại: Giải mã trọn vẹn từ API1 BOLA/IDOR đến API9 Zombie APIs, chiến lược phòng thủ mã nguồn mở trong Spring Boot.

#01 API1:2023 — Broken Object Level Authorization (BOLA / IDOR)

💡 BƯỚC 1: ẨN DỤ ĐỜI THƯỜNG (FEYNMAN ANALOGY)
BOLA trên API giống như khi đi khám bệnh viện: Bác sĩ in kết quả xét nghiệm gửi bạn qua đường link `api.com/v1/patients/105`. Bạn tò mò sửa con số `105` thành `106` trên trình duyệt và bất ngờ nhìn thấy toàn bộ hồ sơ bệnh án riêng tư của một bệnh nhân khác. Máy chủ API quên kiểm tra xem bạn có phải là chủ sở hữu của hồ sơ `106` hay không.
📊 HÌNH HỌA MINH HỌA (VISUAL FLOW): API1:2023 — Broken Object Level Authorization (BOLA / IDOR)
Client Request GET /api/v1/orders/888 API Gateway Token hợp lệ (User ID: 101) Database Query SELECT * WHERE id = 888 Lỗi BOLA Trả về đơn hàng của User 102 ⚠️ OWASP API #1: Lỗ hổng nghiêm trọng và phổ biến nhất trong thế giới API
🧪 BƯỚC 2: PHÒNG THÍ NGHIỆM TƯƠNG TÁC (SANDBOX)
Mẫu thử nghiệm: GET /api/v1/invoices/9999 (Current User: 1234)GET /api/v1/invoices/1234 (Own invoice)
⚠️ Yêu cầu xem hóa đơn 9999 của người khác!
⚖️ BƯỚC 3: SO SÁNH CODE LỖI VS CODE CHUẨN (JAVA / SPRING BOOT)
❌ Vulnerable Java Code (Không an toàn)
// API1 THẢM HỌA: Tra cứu thực thể chỉ bằng ID mà không kiểm tra chủ sở hữu
@GetMapping("/api/v1/orders/{orderId}")
public Order getOrder(@PathVariable Long orderId) {
    return orderRepository.findById(orderId).orElseThrow(); // AI CŨNG ĐỌC ĐƯỢC CỦA NHAU!
}
✅ Secure Java Code (Chuẩn bảo mật)
// CHUẨN AN TOÀN SPRING SECURITY:
@GetMapping("/api/v1/orders/{orderId}")
public ResponseEntity getOrder(@PathVariable Long orderId, 
                                        @AuthenticationPrincipal UserPrincipal user) {
    Order order = orderRepository.findByIdAndOwnerId(orderId, user.getId())
        .orElseThrow(() -> new ResourceNotFoundException("Đơn hàng không tồn tại"));
    return ResponseEntity.ok(convertToDto(order));
}
🎯 BƯỚC 4: THỬ TÀI THẤU HIỂU (ACTIVE CHECK)
Tại sao API Gateway có xác thực JWT hợp lệ vẫn KHÔNG THỂ tự động ngăn chặn được lỗi BOLA?
A. Vì API Gateway chỉ biết token là của người dùng hợp lệ, nhưng chỉ có mã nguồn Backend nghiệp vụ mới biết người dùng đó có quyền sở hữu đối tượng ID cụ thể trong CSDL hay không.
B. Vì API Gateway không hiểu tiếng Anh.
C. Vì JWT không thể mã hóa được số ID.
D. Vì Gateway chỉ hoạt động trên mạng 4G.
📝 Tóm tắt nhanh bằng lời của bạn (Feynman Notebook): (Tự động lưu vào trình duyệt)

#02 API2:2023 — Broken Authentication (Xác thực API bị hỏng)

💡 BƯỚC 1: ẨN DỤ ĐỜI THƯỜNG (FEYNMAN ANALOGY)
Xác thực API bị hỏng giống như chiếc cổng tự động nhận diện khuôn mặt nhưng lại bị lừa bởi một tấm ảnh đen trắng in trên giấy A4 (cho phép token không có chữ ký), hoặc cho phép kẻ gian bấm thử chìa khóa điện tử 1 triệu lần liên tiếp mà cổng không hề phát còi báo động.
📊 HÌNH HỌA MINH HỌA (VISUAL FLOW): API2:2023 — Broken Authentication (Xác thực API bị hỏng)
Endpoint Đăng nhập POST /api/v1/auth/token Credential Stuffing Thử 500 password/giây Không có Rate Limit Hệ thống chấp nhận tất cả Khắc phục Rate Limit + Chữ ký JWT mạnh ⚠️ OWASP API #2: Cơ chế xác thực yếu kém, cho phép đoán token hoặc giả mạo chữ ký
🧪 BƯỚC 2: PHÒNG THÍ NGHIỆM TƯƠNG TÁC (SANDBOX)
Mẫu thử nghiệm: JWT: {"alg": "none", "sub": "admin"}ATTACK: Brute-force OTP 4 digits (0000-9999)JWT: Signed RS256 with Public Key Verification
⚠️ Token JWT sử dụng thuật toán 'none' không có chữ ký bảo vệ!
⚖️ BƯỚC 3: SO SÁNH CODE LỖI VS CODE CHUẨN (JAVA / SPRING BOOT)
❌ Vulnerable Java Code (Không an toàn)
// API2 NGUY HIỂM: Tự sinh token đơn giản không có chữ ký mật mã học
public String createToken(User user) {
    return Base64.encode(user.getId() + ":" + user.getUsername()); // AI CŨNG FAKE ĐƯỢC!
}
✅ Secure Java Code (Chuẩn bảo mật)
// CHUẨN AN TOÀN: Nimbus JWT Encoder với khóa RSA 3072-bit & Expiration
@Bean
public JwtEncoder jwtEncoder(JWKSource jwkSource) {
    return new NimbusJwtEncoder(jwkSource);
}
// Thời gian sống của Access Token ngắn (15 phút) đi kèm Refresh Token xoay vòng
🎯 BƯỚC 4: THỬ TÀI THẤU HIỂU (ACTIVE CHECK)
Biện pháp nào sau đây giúp bảo vệ endpoint xác nhận mã OTP của API khỏi bị tấn công Brute-force?
A. Giới hạn số lần thử tối đa (ví dụ 3-5 lần sai thì hủy mã OTP), đặt thời gian hết hạn ngắn (2-3 phút) và áp dụng Rate Limiting theo IP.
B. Cho phép thử lại không giới hạn số lần.
C. Gửi mã OTP qua đường bưu điện.
D. Bỏ hẳn bước xác nhận OTP.
📝 Tóm tắt nhanh bằng lời của bạn (Feynman Notebook): (Tự động lưu vào trình duyệt)

#03 API3:2023 — Broken Object Property Level Authorization (Mass Assignment & Excessive Data Exposure)

💡 BƯỚC 1: ẨN DỤ ĐỜI THƯỜNG (FEYNMAN ANALOGY)
Lỗ hổng này có hai mặt của một đồng xu: Mặt thứ nhất là nhân viên ngân hàng mang toàn bộ sổ sách nội bộ ghi cả số dư và nợ xấu của người khác ra phát cho khách (Excessive Data Exposure). Mặt thứ hai là khách nộp phiếu đăng ký mở tài khoản nhưng lại tự tay vẽ thêm ô 'Số dư ban đầu: 10 tỷ' và hệ thống ngây thơ ghi luôn 10 tỷ vào máy tính (Mass Assignment).
📊 HÌNH HỌA MINH HỌA (VISUAL FLOW): API3:2023 — Broken Object Property Level Authorization (Mass Assignment & Excessive Data Exposure)
Client Request JSON {isAdmin: true, balance: 999} Mass Assignment Map thẳng vào User Entity Database Save Cập nhật thành công quyền Admin Khắc phục Dùng DTO phân tách rạch ròi ⚠️ OWASP API #3: Gộp chung Mass Assignment và để lộ trường dữ liệu nhạy cảm
🧪 BƯỚC 2: PHÒNG THÍ NGHIỆM TƯƠNG TÁC (SANDBOX)
Mẫu thử nghiệm: PAYLOAD: {"name": "Huy", "role": "ROLE_ADMIN", "credit": 1000000}PAYLOAD: {"name": "Huy"} via Safe DTO
⚠️ Người dùng tự gán thêm thuộc tính 'role' và 'credit' vào request!
⚖️ BƯỚC 3: SO SÁNH CODE LỖI VS CODE CHUẨN (JAVA / SPRING BOOT)
❌ Vulnerable Java Code (Không an toàn)
// API3 THẢM HỌA: Dùng thẳng JPA Entity làm Request Body và Response Body
@PutMapping("/api/v1/users/{id}")
public User updateUser(@PathVariable Long id, @RequestBody User user) {
    return userRepository.save(user); // Cho phép sửa cả role và balance!
}
✅ Secure Java Code (Chuẩn bảo mật)
// CHUẨN AN TOÀN: Luôn dùng DTO riêng biệt cho Input và Output
public record UpdateUserRequest(@Size(min = 2, max = 50) String displayName) {}
public record UserPublicResponse(Long id, String displayName) {}

@PutMapping("/api/v1/users/{id}")
public ResponseEntity updateUser(@PathVariable Long id, 
                                                    @Valid @RequestBody UpdateUserRequest req) {
    User user = userService.updateDisplayName(id, req.displayName());
    return ResponseEntity.ok(new UserPublicResponse(user.getId(), user.getDisplayName()));
}
🎯 BƯỚC 4: THỬ TÀI THẤU HIỂU (ACTIVE CHECK)
Cách phòng thủ triệt để nhất chống lại lỗ hổng Mass Assignment trên REST API là gì?
A. Sử dụng các lớp DTO (Data Transfer Object) chuyên biệt chỉ chứa đúng các trường mà người dùng được phép cập nhật, tuyệt đối không bind trực tiếp JSON vào CSDL Entity.
B. Chỉ dùng phương thức GET.
C. Chuyển sang dùng ngôn ngữ Python.
D. Xóa hết các bảng trong database.
📝 Tóm tắt nhanh bằng lời của bạn (Feynman Notebook): (Tự động lưu vào trình duyệt)

#04 API4:2023 — Unrestricted Resource Consumption (Tiêu thụ Tài nguyên Không giới hạn)

💡 BƯỚC 1: ẨN DỤ ĐỜI THƯỜNG (FEYNMAN ANALOGY)
Bạn mở một tiệm buffet miễn phí. Một vị khách bước vào mang theo 10 chiếc xe tải lớn và đòi múc sạch toàn bộ thức ăn trong bếp mang về nhà (yêu cầu `?size=10000000`). Nếu tiệm buffet không có quy định 'mỗi người chỉ được lấy tối đa 1 đĩa mỗi lượt' (phân trang và rate limit), tiệm của bạn sẽ phá sản ngay trong ngày khai trương.
📊 HÌNH HỌA MINH HỌA (VISUAL FLOW): API4:2023 — Unrestricted Resource Consumption (Tiêu thụ Tài nguyên Không giới hạn)
Client Request GET /api/transactions?size=5000000 Query CSDL Kéo 5 triệu bản ghi vào RAM JVM Crash OutOfMemoryError: Java heap space Khắc phục Bắt buộc Pageable Max Size = 50 ⚠️ OWASP API #4: Bị DoS sập máy chủ do không giới hạn kích thước dữ liệu và tần suất gọi
🧪 BƯỚC 2: PHÒNG THÍ NGHIỆM TƯƠNG TÁC (SANDBOX)
Mẫu thử nghiệm: GET /api/v1/logs?page=0&size=1000000GET /api/v1/logs?page=0&size=20 (Bounded)POST /api/v1/upload (10GB file)
⚠️ Yêu cầu kéo 1.000.000 bản ghi trong 1 lần gọi làm cạn kiệt bộ nhớ RAM!
⚖️ BƯỚC 3: SO SÁNH CODE LỖI VS CODE CHUẨN (JAVA / SPRING BOOT)
❌ Vulnerable Java Code (Không an toàn)
// API4 NGUY HIỂM: Trả về toàn bộ danh sách hoặc tin tưởng tham số size của client
@GetMapping("/api/v1/products")
public List getAll(@RequestParam(required = false) Integer size) {
    if (size != null) return repo.findAll(PageRequest.of(0, size)).getContent();
    return repo.findAll(); // KÉO HÀNG TRIỆU DÒNG GÂY SẬP RAM!
}
✅ Secure Java Code (Chuẩn bảo mật)
// CHUẨN AN TOÀN: Bắt buộc phân trang và áp đặt giới hạn trần cứng (Hard Limit)
@GetMapping("/api/v1/products")
public Page getProducts(
    @RequestParam(defaultValue = "0") int page,
    @RequestParam(defaultValue = "20") int size) {
    // Ép buộc kích thước tối đa không quá 50 phần tử / trang
    int safeSize = Math.min(Math.max(size, 1), 50);
    return productService.findAll(PageRequest.of(page, safeSize));
}
🎯 BƯỚC 4: THỬ TÀI THẤU HIỂU (ACTIVE CHECK)
Những khía cạnh nào cần được giới hạn để ngăn chặn lỗ hổng Unrestricted Resource Consumption trên API?
A. Giới hạn kích thước trang (Page size), thời gian timeout của request, dung lượng tối đa của payload upload, và tần suất gọi request (Rate Limiting).
B. Chỉ cần giới hạn màu sắc của logo.
C. Giới hạn số lượng lập trình viên được quyền deploy.
D. Không cần giới hạn gì cả.
📝 Tóm tắt nhanh bằng lời của bạn (Feynman Notebook): (Tự động lưu vào trình duyệt)

#05 API5:2023 — Broken Function Level Authorization (BFLA)

💡 BƯỚC 1: ẨN DỤ ĐỜI THƯỜNG (FEYNMAN ANALOGY)
Bạn vào một ứng dụng học tập với tư cách là học sinh. Bạn mở công cụ kiểm tra của trình duyệt và thấy một đường link API quản trị: `POST /api/v1/admin/delete-course`. Bạn thử gửi một request POST đến link đó và thấy khóa học bị xóa thật! Máy chủ API chỉ giấu nút bấm trên màn hình của học sinh, chứ không hề có chốt bảo vệ ở cấp độ hàm nghiệp vụ Backend.
📊 HÌNH HỌA MINH HỌA (VISUAL FLOW): API5:2023 — Broken Function Level Authorization (BFLA)
Học sinh (Role: USER) Gửi POST /api/admin/users/5 Thiếu @PreAuthorize Controller không kiểm tra quyền Hàm Admin chạy Xóa tài khoản thành công Khắc phục @PreAuthorize("hasRole('ADMIN')") ⚠️ OWASP API #5: Người dùng thường thực thi được các chức năng quản trị cấp cao
🧪 BƯỚC 2: PHÒNG THÍ NGHIỆM TƯƠNG TÁC (SANDBOX)
Mẫu thử nghiệm: POST /api/v1/admin/upgrade-license (Token: Role USER)POST /api/v1/admin/upgrade-license (Token: Role ADMIN)GET /api/v1/public/health
⚠️ Người dùng với quyền USER đang gọi API của ADMIN!
⚖️ BƯỚC 3: SO SÁNH CODE LỖI VS CODE CHUẨN (JAVA / SPRING BOOT)
❌ Vulnerable Java Code (Không an toàn)
// API5 THẢM HỌA: Endpoint quản trị bỏ quên annotation kiểm tra quyền
@DeleteMapping("/api/v1/admin/users/{id}")
public ResponseEntity deleteUser(@PathVariable Long id) {
    // Bất kỳ ai có token đăng nhập thường đều có thể gọi lệnh này để xóa người khác!
    userService.delete(id);
    return ResponseEntity.ok("Deleted");
}
✅ Secure Java Code (Chuẩn bảo mật)
// CHUẨN AN TOÀN SPRING METHOD SECURITY:
@DeleteMapping("/api/v1/admin/users/{id}")
@PreAuthorize("hasRole('ADMIN') and hasAuthority('SCOPE_user:manage')")
public ResponseEntity deleteUser(@PathVariable Long id) {
    userService.deleteWithAudit(id);
    return ResponseEntity.noContent().build();
}
🎯 BƯỚC 4: THỬ TÀI THẤU HIỂU (ACTIVE CHECK)
Sự khác biệt căn bản giữa BOLA (API1) và BFLA (API5) là gì?
A. BOLA là truy cập trái phép vào dữ liệu/đối tượng của người khác (cùng cấp bậc); BFLA là thực thi trái phép các chức năng/hành động của cấp bậc cao hơn (như người dùng thường gọi chức năng Admin).
B. BOLA xảy ra trên điện thoại, BFLA xảy ra trên máy tính.
C. BFLA chỉ xuất hiện trong Java.
D. Cả hai hoàn toàn giống hệt nhau.
📝 Tóm tắt nhanh bằng lời của bạn (Feynman Notebook): (Tự động lưu vào trình duyệt)

#06 API6:2023 — Unrestricted Access to Sensitive Business Flows (Tiếp cận Không giới hạn Luồng Nghiệp vụ Nhạy cảm)

💡 BƯỚC 1: ẨN DỤ ĐỜI THƯỜNG (FEYNMAN ANALOGY)
Cửa hàng của bạn mở đợt khuyến mãi bán 100 chiếc vé ca nhạc giá rẻ. Một kẻ gian viết một đoạn mã bot tự động bấm nút mua vé 100 lần trong đúng 0.5 giây đầu tiên, ôm trọn toàn bộ vé để mang ra chợ đen bán lại với giá gấp 10 lần. Mọi request của con bot này về mặt kỹ thuật đều hợp lệ (không lỗi cú pháp, không SQLi), nhưng nó phá hủy hoàn toàn luồng nghiệp vụ kinh doanh của bạn.
📊 HÌNH HỌA MINH HỌA (VISUAL FLOW): API6:2023 — Unrestricted Access to Sensitive Business Flows (Tiếp cận Không giới hạn Luồng Nghiệp vụ Nhạy cảm)
Botnet Tự động Gọi API mua vé 1000 lần/s Thiếu Anti-Automation API coi như khách bình thường Vét cạn kho hàng Khách thật không mua được Khắc phục Bot Detection & CAPTCHA/Risk Score ⚠️ OWASP API #6: Lạm dụng tự động hóa phá hoại mô hình kinh doanh (Vé, Giảm giá, Booking)
🧪 BƯỚC 2: PHÒNG THÍ NGHIỆM TƯƠNG TÁC (SANDBOX)
Mẫu thử nghiệm: BOT: 10,000 reservation requests in 2 secondsUSER: Normal checkout flow (1 request)BOT: Automated spam comment on product
⚠️ Botnet tự động vét cạn tài nguyên đặt vé của chương trình khuyến mãi!
⚖️ BƯỚC 3: SO SÁNH CODE LỖI VS CODE CHUẨN (JAVA / SPRING BOOT)
❌ Vulnerable Java Code (Không an toàn)
// API6: Cho phép gọi mua hàng liên tục mà không có cơ chế phát hiện tự động hóa
@PostMapping("/api/v1/flash-sale/buy")
public ResponseEntity buyFlashSale(@RequestBody BuyRequest req) {
    // Bot gọi API này hàng ngàn lần/giây để cướp toàn bộ hàng giảm giá!
    return flashSaleService.process(req);
}
✅ Secure Java Code (Chuẩn bảo mật)
// CHUẨN AN TOÀN: Kiểm soát luồng kinh doanh bằng Rate Limit & Bằng chứng xác thực người
@PostMapping("/api/v1/flash-sale/buy")
public ResponseEntity buyFlashSale(@Valid @RequestBody BuyRequest req,
                                     @RequestHeader("X-Turnstile-Token") String captchaToken,
                                     @AuthenticationPrincipal UserPrincipal user) {
    // 1. Xác thực bằng chứng không phải bot (Cloudflare Turnstile)
    botDetectionService.verifyHuman(captchaToken);
    // 2. Ràng buộc nghiệp vụ: Mỗi CMND/User chỉ được mua tối đa 1 sản phẩm trong đợt sale
    flashSaleService.processWithStrictBusinessLimit(user.getId(), req.getProductId());
    return ResponseEntity.ok("Success");
}
🎯 BƯỚC 4: THỬ TÀI THẤU HIỂU (ACTIVE CHECK)
Biện pháp nào sau đây giúp phòng vệ hiệu quả nhất trước lỗ hổng API6 (Lạm dụng luồng nghiệp vụ nhạy cảm)?
A. Kết hợp phát hiện Bot tự động (Device Fingerprinting/Turnstile), giới hạn tỷ lệ theo nghiệp vụ (chỉ 1 giao dịch/người), và phân tích hành vi bất thường.
B. Xóa API đó đi.
C. Chỉ phục vụ khách hàng quen biết.
D. Chuyển toàn bộ dữ liệu sang file Excel.
📝 Tóm tắt nhanh bằng lời của bạn (Feynman Notebook): (Tự động lưu vào trình duyệt)

#07 API7:2023 — Server Side Request Forgery in APIs (SSRF trên API)

💡 BƯỚC 1: ẨN DỤ ĐỜI THƯỜNG (FEYNMAN ANALOGY)
API của bạn có chức năng cho phép người dùng nhập link Webhook để nhận thông báo: `POST /api/webhooks {url: '...'}`. Thay vì nhập link của hệ thống của họ, kẻ tấn công nhập `http://169.254.169.254/latest/meta-data/` hoặc `http://192.168.1.50/admin`. Máy chủ API của bạn ngoan ngoãn gửi HTTP request tới các địa chỉ đó và bắn ngược dữ liệu nhạy cảm nội bộ về cho kẻ tấn công.
📊 HÌNH HỌA MINH HỌA (VISUAL FLOW): API7:2023 — Server Side Request Forgery in APIs (SSRF trên API)
Client Webhook url: http://internal-vault:8200 API Backend Gửi HTTP Request Private Network Nội bộ bị chọc thủng Khắc phục Cô lập Network & Egress Filtering ⚠️ OWASP API #7: Webhook và tích hợp bên thứ ba bị lợi dụng để quét mạng nội bộ
🧪 BƯỚC 2: PHÒNG THÍ NGHIỆM TƯƠNG TÁC (SANDBOX)
Mẫu thử nghiệm: WEBHOOK_URL: http://169.254.169.254/latest/meta-data/WEBHOOK_URL: http://10.0.0.5/internal-apiWEBHOOK_URL: https://api.slack.com/webhook/safe
⚠️ Webhook trỏ vào địa chỉ IP nội bộ của Cloud Metadata!
⚖️ BƯỚC 3: SO SÁNH CODE LỖI VS CODE CHUẨN (JAVA / SPRING BOOT)
❌ Vulnerable Java Code (Không an toàn)
// API7 THẢM HỌA: Cho phép gửi Webhook tới bất kỳ URL nào
@PostMapping("/api/v1/webhooks")
public ResponseEntity registerWebhook(@RequestBody WebhookDto dto) {
    // Không kiểm tra IP/domain của dto.getUrl()
    webhookSenderService.sendPing(dto.getUrl()); // BỊ DÍNH SSRF!
    return ResponseEntity.ok("Registered");
}
✅ Secure Java Code (Chuẩn bảo mật)
// CHUẨN AN TOÀN: Sử dụng bộ gửi Webhook cô lập trong Subnet riêng biệt có Egress Firewall
public void sendSafeWebhook(String targetUrl, String payload) throws Exception {
    URI uri = URI.create(targetUrl);
    if (!"https".equalsIgnoreCase(uri.getScheme())) {
        throw new IllegalArgumentException("Chỉ chấp nhận HTTPS Webhook");
    }
    InetAddress addr = InetAddress.getByName(uri.getHost());
    if (addr.isSiteLocalAddress() || addr.isLoopbackAddress() || addr.isLinkLocalAddress()) {
        throw new SecurityException("Webhook không được phép trỏ tới địa chỉ nội bộ!");
    }
    // Thực hiện gửi request với timeout ngắn 3 giây
}
🎯 BƯỚC 4: THỬ TÀI THẤU HIỂU (ACTIVE CHECK)
Tính năng phổ biến nào trong các ứng dụng Web/API hiện đại dễ tiềm ẩn nguy cơ SSRF (API7) nhất?
A. Tính năng đăng ký Webhook, tính năng tải ảnh đại diện từ link URL, và tính năng xem trước liên kết (Link preview).
B. Tính năng đổi mật khẩu.
C. Tính năng đăng xuất.
D. Tính năng đếm số lượng người online.
📝 Tóm tắt nhanh bằng lời của bạn (Feynman Notebook): (Tự động lưu vào trình duyệt)

#08 API8:2023 — Security Misconfiguration in APIs (Cấu hình Sai Bảo mật trên API)

💡 BƯỚC 1: ẨN DỤ ĐỜI THƯỜNG (FEYNMAN ANALOGY)
API của bạn được ví như một cổng giao tiếp điện tử tự động, nhưng bạn lại quên bật mã hóa HTTPS (chạy HTTP thường), bật cờ CORS cho phép mọi trang web lạ đọc dữ liệu (`*`), và khi API bị lỗi cú pháp thì nó in luôn toàn bộ dòng code và câu truy vấn CSDL cho khách xem.
📊 HÌNH HỌA MINH HỌA (VISUAL FLOW): API8:2023 — Security Misconfiguration in APIs (Cấu hình Sai Bảo mật trên API)
Client API Call Origin: https://evil.com CORS Misconfig Access-Control-Allow-Origin: * Rò rỉ dữ liệu Trang web lạ đọc trộm Token Khắc phục CORS Allowlist nghiêm ngặt ⚠️ OWASP API #8: Cấu hình CORS lỏng lẻo, thiếu Security Headers, bật Debug và Verb Tampering
🧪 BƯỚC 2: PHÒNG THÍ NGHIỆM TƯƠNG TÁC (SANDBOX)
Mẫu thử nghiệm: CORS: Access-Control-Allow-Origin: * with Allow-Credentials: trueCORS: Strict Origin Allowlist via Spring CorsConfigurationERROR: Return full Java Stack Trace to client
⚠️ Cấu hình CORS wildcard '*' đi kèm cho phép gửi thông tin định danh!
⚖️ BƯỚC 3: SO SÁNH CODE LỖI VS CODE CHUẨN (JAVA / SPRING BOOT)
❌ Vulnerable Java Code (Không an toàn)
// API8 NGUY HIỂM: Cấu hình CORS mở toàn bộ và cho phép HTTP thuần
@CrossOrigin(origins = "*") // Nguy cơ rò rỉ dữ liệu chéo trang
@RestController
public class FinanceApiController { ... }
✅ Secure Java Code (Chuẩn bảo mật)
// CHUẨN AN TOÀN SPRING BOOT:
@Bean
public CorsConfigurationSource corsConfigurationSource() {
    CorsConfiguration config = new CorsConfiguration();
    config.setAllowedOrigins(List.of("https://dashboard.mycompany.com"));
    config.setAllowedMethods(List.of("GET", "POST", "PUT", "DELETE"));
    config.setAllowedHeaders(List.of("Authorization", "Content-Type"));
    config.setAllowCredentials(true);
    UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
    source.registerCorsConfiguration("/api/**", config);
    return source;
}
🎯 BƯỚC 4: THỬ TÀI THẤU HIỂU (ACTIVE CHECK)
Header nào sau đây thể hiện một cấu hình CORS cực kỳ nguy hiểm cho API có chứa dữ liệu riêng tư?
A. Access-Control-Allow-Origin: * (hoặc phản chiếu động bất kỳ Origin nào gửi lên) đi kèm Access-Control-Allow-Credentials: true.
B. Content-Type: application/json.
C. X-Content-Type-Options: nosniff.
D. Strict-Transport-Security.
📝 Tóm tắt nhanh bằng lời của bạn (Feynman Notebook): (Tự động lưu vào trình duyệt)

#09 API9:2023 — Improper Inventory Management (Quản lý Kho API Thiếu Chặt chẽ: Shadow & Zombie APIs)

💡 BƯỚC 1: ẨN DỤ ĐỜI THƯỜNG (FEYNMAN ANALOGY)
Bạn xây một ngôi nhà mới khang trang có khóa số điện tử thông minh ở cửa chính (API v2). Nhưng bạn lại bỏ quên một cánh cửa gỗ cũ nát từ thời ông bà để lại ở góc vườn sau (API v1 cũ kỹ từ 5 năm trước không ai bảo trì). Kẻ trộm chẳng dại gì đập cửa chính thông minh, hắn chỉ cần đi vòng ra sau vườn đẩy nhẹ cánh cửa gỗ mục nát để vào nhà.
📊 HÌNH HỌA MINH HỌA (VISUAL FLOW): API9:2023 — Improper Inventory Management (Quản lý Kho API Thiếu Chặt chẽ: Shadow & Zombie APIs)
API v2 (Hiện đại) Có MFA, Rate Limit, WAF API v1 (Zombie) Không MFA, dính SQLi cũ Hacker phát hiện Tấn công qua API v1 bị bỏ quên Khắc phục Quản lý OpenAPI & Decommission v1 ⚠️ OWASP API #9: Các API cũ (Zombie) hoặc API thử nghiệm không được ghi chép (Shadow)
🧪 BƯỚC 2: PHÒNG THÍ NGHIỆM TƯƠNG TÁC (SANDBOX)
Mẫu thử nghiệm: ENDPOINT: /api/v1/users/login (Deprecated 4 years ago, still running)ENDPOINT: /api/staging-test/reset (Forgotten shadow endpoint)MANAGEMENT: Active Inventory with OpenAPI & Sunset Header
⚠️ Endpoint API v1 cũ kỹ vẫn đang chạy ngầm và không được vá lỗi!
⚖️ BƯỚC 3: SO SÁNH CODE LỖI VS CODE CHUẨN (JAVA / SPRING BOOT)
❌ Vulnerable Java Code (Không an toàn)
// API9 THẢM HỌA: Bỏ quên Controller phiên bản cũ trên Production
@RestController
@RequestMapping("/api/v1/auth") // API CŨ TỪ 2019 BỎ QUÊN KHÔNG BẢO TRÌ!
public class LegacyAuthController {
    // Vẫn dùng MD5 và không có Rate Limit!
}
✅ Secure Java Code (Chuẩn bảo mật)
// CHUẨN AN TOÀN: Quản lý vòng đời API và trả về Header Sunset / 410 Gone
@RestController
@RequestMapping("/api/v1/auth")
public class DeprecatedAuthController {
    @PostMapping("/**")
    public ResponseEntity deprecated() {
        return ResponseEntity.status(HttpStatus.GONE) // HTTP 410 GONE
            .header("Sunset", "Wed, 01 Jan 2025 00:00:00 GMT")
            .body("Phiên bản API v1 đã bị khai tử hoàn toàn. Vui lòng nâng cấp lên /api/v2/");
    }
}
🎯 BƯỚC 4: THỬ TÀI THẤU HIỂU (ACTIVE CHECK)
Khái niệm 'Zombie API' dùng để chỉ điều gì?
A. Các endpoint API phiên bản cũ đáng lẽ phải bị tắt bỏ nhưng vẫn âm thầm chạy trên máy chủ mà không có ai bảo trì hay cập nhật bảo mật.
B. API chỉ hoạt động vào ban đêm.
C. API chuyên về trò chơi điện tử.
D. API bị nhiễm virus phần cứng.
📝 Tóm tắt nhanh bằng lời của bạn (Feynman Notebook): (Tự động lưu vào trình duyệt)