IDOR – Lỗ hổng “ngây thơ” nhưng chí mạng trong kiến trúc phần mềm?

Hãy tưởng tượng bạn check-in vào một khách sạn 5 sao sang trọng. Hệ thống an ninh tối tân, camera khắp nơi, thẻ từ thang máy hiện đại. Lễ tân đưa cho bạn chìa khóa phòng 101. Bạn lên phòng, mở cửa và nghỉ ngơi. Nhưng sau đó, vì tò mò, bạn thử cạo sửa con số trên chìa khóa thành 102 và cắm vào ổ khóa phòng bên cạnh. Cạch! Cửa mở toang.

Trong thế giới thực, điều này thật nực cười. Nhưng trong thế giới lập trình Web và API, tình huống này xảy ra thường xuyên đến mức nó trở thành “cơn đau đầu” kinh niên của giới bảo mật. Nó được gọi là IDOR (Insecure Direct Object Reference) hay BOLA (Broken Object Level Authorization).

Nếu bạn là một Backend Developer, và bạn tin rằng chỉ cần có Authentication (Đăng nhập) là hệ thống đã an toàn, thì bài viết này sẽ thay đổi tư duy lập trình của bạn. Chúng ta sẽ không chỉ nói về bề nổi, mà sẽ đi sâu vào bản chất kiến trúc của lỗi này.

1. Bản chất kỹ thuật của IDOR

IDOR (Insecure Direct Object Reference) không chỉ là một lỗi code, nó là một lỗi về thiết kế Logic (Business Logic Flaw).

Trong danh sách OWASP API Security Top 10, IDOR chính là bản chất của lỗ hổng số 1: API1: 2023 Broken Object Level Authorization (BOLA).

Hiểu sâu hơn, IDOR xảy ra khi 3 điều kiện sau hội tụ:

  1. Direct Reference: Ứng dụng để lộ mã định danh nội bộ (Internal Identifier) của đối tượng (như Database Primary Key, Filename) trực tiếp ra ngoài URL hoặc Body request.
  2. User Input: Ứng dụng tin tưởng và sử dụng trực tiếp đầu vào này để truy vấn dữ liệu.
  3. Missing Authorization: Ứng dụng bỏ qua bước kiểm tra quyền truy cập tại thời điểm truy xuất đối tượng.

Sai lầm phổ biến: Nhiều Dev nhầm lẫn giữa Authentication (Bạn là ai?) và Authorization (Bạn được làm gì?). IDOR xảy ra khi bạn đã qua cửa Authentication, nhưng hệ thống quên kiểm tra Authorization ở mức độ đối tượng (Object-level).

2. Giải phẫu cơ chế tấn công

Hãy mổ xẻ một HTTP Request để thấy IDOR có thể lẩn trốn ở đâu. Không chỉ ở URL Parameter, nó có thể nằm ở Header hoặc Body.

Kịch bản: Ứng dụng Chat cho phép người dùng tải lại lịch sử chat.

Request bình thường:

GET /api/v1/messages/history HTTP/1.1
Host: example.com
Cookie: session_id=abc123xyz...
X-User-ID: 1005  <-- IDOR tiềm ẩn ở Header
  1. Hacker quan sát: Thấy Header X-User-ID: 1005 được gửi kèm mỗi request.
  2. Thử nghiệm: Hacker dùng Postman hoặc Burp Suite để chặn request, sửa X-User-ID thành 1006.
  3. Kết quả: Server, thay vì lấy ID từ session an toàn (Server-side), lại tin tưởng lấy ID từ Header (Client-side) để query database SELECT * FROM messages WHERE user_id = 1006.
  4. Hậu quả: Hacker đọc được toàn bộ tin nhắn của người dùng 1006.

IDOR có thể xuất hiện ở:

  • URL Path: /users/100/profile
  • Query Params: /invoices?id=100
  • Body (JSON/XML): POST /api/change-password với body {"user_id": 100, "new_pass": "..."}

3. Tại sao IDOR lại là “Sát thủ thầm lặng”?

IDOR nguy hiểm vì tính “hợp lệ” về mặt cú pháp của nó.

  • Vượt mặt WAF (Web Application Firewall): Các tường lửa thường chặn các ký tự lạ như ' OR 1=1 -- (SQL Injection) hay <script> (XSS). Nhưng với IDOR, payload chỉ là một con số: id=1006. Đối với WAF, đây là một con số hoàn toàn sạch và hợp lệ.
  • Khó phát hiện bằng Automation Tool: Các máy quét (Scanner) không hiểu ngữ cảnh nghiệp vụ. Nếu máy quét gửi id=1006 và nhận về 200 OK, nó đánh dấu là “Thành công”. Nó không biết rằng dữ liệu trả về đó không thuộc về người đang gửi request. Chỉ có con người (Pentesters) hoặc Unit Test được viết kỹ lưỡng mới phát hiện ra.
  • Hệ quả dây chuyền: IDOR thường là bước đệm để leo thang đặc quyền (Privilege Escalation), dẫn đến việc chiếm đoạt toàn bộ hệ thống (Account Takeover).

Sự nguy hiểm của IDOR nằm ở chỗ nó là một lỗ hổng logic, không phải lỗ hổng cú pháp. Các công cụ quét bảo mật tĩnh (SAST) truyền thống thường thất bại trong việc phát hiện IDOR vì chúng không thể hiểu ngữ cảnh “ai sở hữu cái gì” trong một ứng dụng cụ thể. Điều này dẫn đến một nghịch lý: trong khi các lỗ hổng kỹ thuật như SQL Injection đang giảm dần nhờ các framework hiện đại, IDOR vẫn tồn tại dai dẳng và chiếm tỷ lệ cao trong các chương trình săn lỗi nhận thưởng (Bug Bounty), đặc biệt là trong các lĩnh vực y tế, chính phủ và dịch vụ chuyên nghiệp.

4. Phân loại IDOR chuyên sâu.

4.1. Leo thang đặc quyền ngang (Horizontal Escalation)

Đây là dạng phổ biến nhất. Người dùng A truy cập dữ liệu của Người dùng B có cùng vai trò.

  • Ví dụ: Xem đơn hàng, tải hóa đơn, xem ảnh riêng tư.
  • Rủi ro: Rò rỉ thông tin cá nhân (PII Breach), vi phạm GDPR/CCPA.

4.2. Leo thang đặc quyền dọc (Vertical Escalation)

Người dùng thường (User) tìm cách gọi các API dành riêng cho Quản trị viên (Admin/Manager).

  • Ví dụ:
    • Hacker tìm thấy API: POST /api/users/1001/roles.
    • Hacker gửi request với body: {"role": "ADMIN"}.
    • Nếu Backend không check xem “người gọi API này có phải là Super Admin không”, Hacker lập tức trở thành Admin.

4.3. IDOR trên File tĩnh (Static File IDOR)

Tên file thường dễ đoán.

  • Ví dụ: https://example.com/uploads/receipts/receipt_001.pdf.
  • Hacker viết script lặp từ 001 đến 9999 để tải toàn bộ hóa đơn của hệ thống.

5. Dẫn chứng thực tế: Khi “Ông lớn” cũng mắc sai lầm

5.1. McDonald’s & sự cố McHire (7/2025)

Link: https://research.cgu.edu/icdc/2025/07/01/mcdonalds-july-2025-breach/

Bối cảnh: Hệ thống bị ảnh hưởng là McHire, nền tảng tuyển dụng toàn cầu của McDonald’s, được vận hành bởi đối tác công nghệ bên thứ ba là Paradox.ai. Paradox.ai cung cấp giải pháp chatbot AI tên là “Olivia” để tự động hóa quy trình phỏng vấn, thu thập thông tin ứng viên và lên lịch làm việc. Sự phụ thuộc vào bên thứ ba này đã mở rộng bề mặt tấn công của McDonald’s ra ngoài hạ tầng kiểm soát trực tiếp của họ.   

Nguyên nhân chính
Sự cố xảy ra do kết hợp 2 lỗi nghiêm trọng:

  1. Xác thực yếu (Weak Authentication)
    • Một tài khoản admin thử nghiệm của Paradox.ai dùng mật khẩu “123456”, không có MFA.
    • Tài khoản này tồn tại từ 2019, không bị vô hiệu hóa dù có quyền truy cập production.
  2. Lỗ hổng IDOR (Missing Object Level Authorization)
    • API backend cho phép truy xuất hồ sơ ứng viên bằng applicant_id.
    • Không kiểm tra quyền truy cập → chỉ cần đổi ID là đọc được dữ liệu của bất kỳ ứng viên nào.
    • Từ 1 tài khoản → mở rộng ra 64 triệu hồ sơ toàn cầu.

Tác động

  • Rò rỉ PII: tên, email, số điện thoại, địa chỉ nhà.
  • Lộ dữ liệu nhạy cảm: log chat với AI (tính cách, sở thích, thông tin cá nhân).
  • Lộ token phiên → có thể giả danh ứng viên.
  • Nguy cơ cao cho social engineering, lừa đảo, chiếm tài khoản.

Sự kết hợp giữa quản lý tài khoản kém (tài khoản ma, không MFA) và thiếu kiểm soát quyền API (IDOR) tạo ra “cơn bão hoàn hảo” cho rò rỉ dữ liệu quy mô toàn cầu – một xu hướng cực kỳ đáng báo động trong năm 2025

5.2. Mozilla Firefox Accounts – Lỗ hổng xóa tài khoản (5/2025)

Link: https://hackerone.com/reports/3154983

Bản chất sự cố

  • Lỗ hổng IDOR nghiêm trọng trong API xóa tài khoản (/v1/account/destroy).
  • Cho phép xóa vĩnh viễn tài khoản người khác mà không cần tương tác từ nạn nhân (zero-click).
  • Ảnh hưởng trực tiếp đến Integrity (toàn vẹn) và Availability (sẵn sàng) của dữ liệu.

Nguyên nhân kỹ thuật

  • Lỗi Session Misbinding: backend không kiểm tra phiên làm việc có khớp với tài khoản bị xóa hay không.
  • API tin vào payload JSON (email, authPW) thay vì session của người gửi request.
  • Đây là dạng IDOR ghi/xóa, nguy hiểm hơn IDOR đọc dữ liệu.

Kịch bản khai thác

  1. Kẻ tấn công tạo tài khoản Firefox, đăng nhập bằng SSO (Google).
  2. Chặn request xóa tài khoản của chính mình.
  3. Đổi email trong payload thành email nạn nhân (cũng dùng SSO, chưa set mật khẩu).
  4. Gửi request → hệ thống xóa tài khoản nạn nhân.

Tác động

  • Mất vĩnh viễn dữ liệu: mật khẩu đã lưu, lịch sử duyệt web, tab, sync data.
  • Không thể khôi phục, không cần nạn nhân click hay xác nhận.
  • Được đánh giá Critical trên HackerOne.

Bài học cốt lõi: IDOR không chỉ gây rò rỉ dữ liệu (read) mà còn có thể phá hủy dữ liệu (write/delete) nếu backend không ràng buộc chặt chẽ giữa session và đối tượng tài nguyên.

5.3. GitLab – Lỗ hổng Machine Learning Model Registry (2024–2025)

Link: https://hackerone.com/reports/2528293

Bối cảnh

  • GitLab triển khai Machine Learning Model Registry để quản lý phiên bản mô hình AI.
  • Lỗ hổng được báo cáo từ 5/2024, vá và công bố kéo dài sang 2025, cho thấy độ phức tạp của bảo mật trên nền tảng lớn.
  • Sự cố ảnh hưởng trực tiếp đến tài sản trí tuệ AI (model weights, biases).

Nguyên nhân kỹ thuật

  • GitLab dùng ID tịnh tiến (1,2,3,…) cho model_id thay vì UUID ngẫu nhiên.
  • API tải/xem mô hình không kiểm tra quyền truy cập dự án (Missing Object Level Authorization).
  • Đây là IDOR điển hình + thiết kế API kém an toàn.

Cách khai thác

  • Kẻ tấn công chỉ cần tài khoản GitLab miễn phí.
  • Lặp ID từ 1 → hàng triệu để liệt kê và tải mô hình của dự án khác.
  • Không cần brute-force, không cần quyền dự án.

Tác động

  • Rò rỉ mô hình ML độc quyền của doanh nghiệp.
  • Nguy cơ mất bí mật thương mại, thiệt hại kinh tế lớn trong bối cảnh AI là tài sản cốt lõi.
  • Lỗ hổng có thể bị khai thác tự động, quy mô lớn (enumeration).

Bài học cốt lõi: Không dùng ID tịnh tiến cho API công khai. Nó biến IDOR từ lỗ hổng tiềm ẩn thành thảm họa có thể khai thác hàng loạt, đặc biệt nguy hiểm với tài sản AI.

6. Ngăn chặn IDOR hiệu quả (Best Practices)

Khi một tài khoản test có thể mở khóa 64 triệu hồ sơ, một email có thể xóa tài khoản người khác, và một con số tăng dần có thể làm bay cả mô hình AI, thì rõ ràng vấn đề không nằm ở “hacker quá giỏi” — mà ở chỗ chúng ta cần chặn IDOR cho đúng cách.

“Đừng bao giờ tin tưởng Client”. Dưới đây là chiến lược phòng thủ đa lớp (Defense in Depth).

Lớp 1: Kiểm soát truy cập tại tầng Data (Data-Layer Authorization)

Đây là chốt chặn cuối cùng và quan trọng nhất. Mọi câu lệnh truy vấn liên quan đến dữ liệu người dùng phải kèm theo điều kiện về chủ sở hữu.

❌ Code rủi ro (Spring Data JPA):

Java
// Controller
public Order getOrder(@PathVariable Long id) {
    return orderRepo.findById(id); // Chỉ tìm theo ID đơn hàng
}

✅ Code an toàn (Repository Pattern):

Java
// Repository
// Luôn gắn kèm userId vào câu query
@Query("SELECT o FROM Order o WHERE o.id = :id AND o.owner.id = :currentUserId")
Optional<Order> findByIdAndOwner(@Param("id") Long id, @Param("currentUserId") Long userId);

// Service Layer
public Order getOrder(Long id, User currentUser) {
    return orderRepo.findByIdAndOwner(id, currentUser.getId())
        .orElseThrow(() -> new ResourceNotFoundException("Order not found or access denied"));
}

Lớp 2: Kiểm soát truy cập tại tầng Controller (Method-Level Security)

Sử dụng các Annotation bảo mật của Framework (như Spring Security) để chặn ngay từ cửa vào.

✅ Ví dụ với Spring Security (@PreAuthorize):

Java
@GetMapping("/invoices/{id}")
// SpEL: Chỉ cho phép nếu user hiện tại là chủ sở hữu của invoice có id này
@PreAuthorize("@securityService.isOwner(authentication, #id)") 
public Invoice getInvoice(@PathVariable Long id) {
    return invoiceRepo.findById(id);
}

Cách này giúp code business logic sạch hơn, tách biệt logic bảo mật ra khỏi logic nghiệp vụ.

Lớp 3: Làm khó Hacker bằng UUID (Obfuscation)

Thay vì dùng Auto-Increment Integer (1, 2, 3), hãy dùng UUID (Random String: a0eebc99-9c0b...) hoặc các thuật toán băm ID (như Hashids).

  • Lợi ích: Chặn đứng tấn công kiểu liệt kê (Enumeration Attack). Hacker không thể viết vòng lặp for (i=0; i<1000; i++) để quét dữ liệu.
  • Cảnh báo: UUID KHÔNG THỂ THAY THẾ được Lớp 1Lớp 2. Nếu hacker bằng cách nào đó biết được UUID của nạn nhân, IDOR vẫn xảy ra nếu thiếu Authorization check. UUID chỉ là “lớp sương mù”, không phải “bức tường thép”.

Lớp 4: Kiểm thử tự động (Automated Security Testing)

Đừng đợi Pentester tìm lỗi. Hãy viết Test Case cho IDOR.

  • Unit Test: Tạo 2 user (Alice, Bob). Đăng nhập bằng Alice, cố gắng request dữ liệu ID của Bob. Assert rằng kết quả trả về phải là 403 Forbidden hoặc 404 Not Found.

7. Kết luận

IDOR không phải là một lỗi kỹ thuật phức tạp, nhưng nó phản ánh sự thiếu sót trong tư duy thiết kế hệ thống.

Hãy nhớ nguyên tắc vàng: Mọi request truy cập dữ liệu đều phải trả lời được hai câu hỏi:

  1. Người dùng này là ai? (Authentication)
  2. Dữ liệu này có phải của họ không? (Authorization)

Nếu thiếu câu hỏi số 2, chúng ta đang mở toang cánh cửa “phòng 102” cho bất kỳ ai cầm chìa khóa “phòng 101”.

0 Shares:
Leave a Reply

Your email address will not be published. Required fields are marked *

You May Also Like
Read More

Setup slack notifications in Rails

Table of Contents Hide Setup Slack:Setup Rails: Slack là một phần mềm Worksplace sử dụng thông dụng rộng…