Product Management
Đăng nhập
ESC

Nhập từ khóa để tìm kiếm

↑↓ Di chuyển
Enter Mở
ESC Đóng

Bài 3 — Phát hiện SQL Injection bằng taint tracking với LLM

Đã đọc 0/9 mục · còn khoảng 4 phút
Cách chúng tôi ghi nhận

Một mục được tính là đã đọc khi nó nằm giữa màn hình đủ lâu so với độ dài của nó (bảng và sơ đồ tính một nửa), bạn vẫn đang cuộn hoặc bấm phím, và bạn đã thấy tới cuối mục. Lướt nhanh hoặc để tab mở không được tính. Chúng tôi chỉ lưu thời gian đọc từng mục — không lưu gì khác.

SQL Injection (SQLi) xảy ra khi dữ liệu người dùng chui vào câu SQL dưới dạng mã thay vì dữ liệu. Bài này dạy agent theo dấu dữ liệu bẩn (taint tracking) xuyên hàm, xuyên file — điều mà pattern matching thuần rất khó.

1. Ba trạng thái taint

Với mỗi biến, agent gán một trong ba nhãn:

  • tainted: đến từ nguồn người dùng, chưa làm sạch.
  • sanitized: đã qua prepared statement / ép kiểu / whitelist.
  • safe: hằng số hoặc dữ liệu nội bộ tin cậy.
Lỗ hổng = biến tainted chạm sink SQL.

2. Ví dụ xuyên hàm

// controllers/UserController.php
function search() {
  $q = $_GET['q'];          // TAINTED
  return User::find($q);    // truyền vào model
}

// models/User.php
function find($keyword) {   // $keyword vẫn TAINTED
  global $db;
  return $db->query(
    "SELECT * FROM users WHERE name LIKE '%$keyword%'"  // SINK
  );
}

Semgrep nhìn models/User.php một mình sẽ thấy $keyword "không rõ nguồn" và có thể bỏ qua. Agent LLM, khi được cấp tool đọc caller, sẽ nối được: $_GET['q'] → find() → câu SQL nối chuỗi → SQLi.

3. Prompt taint có định hướng

Nhiệm vụ: taint tracking cho SQLi.
Bước 1: liệt kê mọi SOURCE (đầu vào người dùng) trong đoạn code.
Bước 2: với mỗi SOURCE, lần theo các phép gán/biến đổi/hàm gọi.
Bước 3: đánh dấu SANITIZED nếu đi qua: prepared statement,
        binding tham số, ép kiểu số (intval/(int)), hoặc whitelist.
Bước 4: nếu một biến TAINTED tới một câu SQL nối chuỗi -> báo SQLi.
Trả JSON: {"trace": [...các bước...], "findings": [...]}.
Nếu không thấy định nghĩa của hàm được gọi, ghi cần_đọc_thêm: <tên hàm>.

Trường cần_đọc_thêm biến agent thành vòng lặp: nếu thiếu ngữ cảnh, nó yêu cầu thêm thay vì đoán bừa.

4. Vòng lặp cấp ngữ cảnh (tool use)

def scan_with_context(entry_file, resolver):
    ctx = read(entry_file)
    for _ in range(3):                 # tối đa 3 vòng lấy thêm
        out = ask(TAINT_PROMPT, ctx)
        need = out.get("can_doc_them")
        if not need:
            return out["findings"]
        ctx += "\n\n" + resolver(need)  # nạp định nghĩa hàm còn thiếu
    return out["findings"]

resolver là tool tra định nghĩa hàm trong repo (grep/ctags/LSP). Giới hạn 3 vòng để tránh loop vô hạn và tốn token.

5. Nhận diện sanitizer THẬT vs GIẢ

LLM giỏi phân biệt sanitizer đúng loại:

$id = htmlspecialchars($_GET['id']);        // GIẢ cho SQLi!
$db->query("... WHERE id = $id");           // vẫn SQLi

htmlspecialchars chống XSS, không chống SQLi. Đây là lỗi kinh điển mà rule cứng hay bỏ sót; hãy nhấn mạnh trong prompt: sanitizer phải đúng loại lỗ hổng.

Một dạng sanitizer giả nữa hay gặp là "làm sạch" bằng cách thay dấu nháy đơn thủ công, ví dụ str_replace("'", "''", $x). Cách này rò rỉ trong nhiều tình huống (unicode, backslash, chế độ SQL khác nhau) và không bao giờ đáng tin bằng prepared statement. Agent cần được dạy rằng chỉ có binding tham số ở tầng driver mới thực sự tách dữ liệu khỏi mã. Tương tự, blacklist kiểu "chặn từ khóa SELECT/UNION" là chống chỉ định — kẻ tấn công luôn có biến thể vượt qua. Nguyên tắc: ưu tiên whitelist và prepared statement, nghi ngờ mọi cách làm sạch tự chế.

6. Các bước triển khai

  • Viết SOURCES ($_GET, $_POST, $_REQUEST, php://input...).
  • Viết SQL_SINKS (->query, ->raw, mysqli_query, chuỗi có SELECT/INSERT/UPDATE/DELETE).
  • Cấp resolver để đọc hàm được gọi.
  • Chạy taint prompt, giới hạn 3 vòng ngữ cảnh.
  • Chỉ giữ finding có prepared-statement bị thiếu.
  • Chuyển sang bài phản biện để loại false positive.

Checklist SQLi

  • [ ] Đã liệt kê source đầy đủ cho ngôn ngữ/framework?
  • [ ] Prepared statement / binding được coi là sanitized?
  • [ ] Ép kiểu số được coi là sanitized cho tham số số?
  • [ ] Phân biệt sanitizer đúng loại (không nhầm XSS-escape là SQL-safe)?
  • [ ] Có lần theo qua ORM raw query?

Sai lầm thường gặp

  • Nhầm escape HTML là chống SQLi: hai loại sanitizer khác nhau.
  • Bỏ qua ORM: ->raw(), whereRaw(), DB::select() vẫn nối chuỗi được.
  • Chỉ quét một file: mất taint xuyên tầng; luôn cấp resolver.
  • Coi mọi $_GET chạm SQL là lỗi: nếu đã bind tham số thì an toàn — đừng báo.
Bài sau: XSS và IDOR — hai loại lỗ hổng cần ngữ cảnh khác nhau.

Học xong bài này rồi? Tạo tài khoản miễn phí để lưu lại — lần sau vào là biết ngay đang dở ở đâu, và học hết khóa thì có chứng chỉ. Lưu tiến độ của tôi