Ba lỗ hổng web kinh điển: SQL Injection, XSS và CSRF

  • author-image

    Nguyễn Đình Cường

  • blog-tag
  • blog-comment 0 comment
  • created-date 09 Nov, 2026
blog-thumbnail

Ba cái tên SQL Injection, XSS và CSRF xuất hiện trong gần như mọi danh sách lỗ hổng web phổ biến. Điều đáng mừng là chúng đều phòng được bằng những thói quen khá đơn giản. Bài này giải thích từng lỗ hổng và cách chặn ở mức mà lập trình viên backend nào cũng cần nắm.

SQL Injection

Xảy ra khi bạn ghép dữ liệu người dùng trực tiếp vào câu SQL. Ví dụ code dễ bị tấn công:

// KHÔNG LÀM THẾ NÀY$sql = "SELECT * FROM users WHERE email = '" . $_GET['email'] . "'";

Nếu kẻ tấn công nhập ' OR '1'='1, điều kiện luôn đúng và toàn bộ người dùng bị lộ; những biến thể nặng hơn có thể sửa hoặc xóa dữ liệu. Cách phòng chống chính là câu lệnh có tham số (prepared statement), tách riêng câu lệnh và dữ liệu:

$stmt = $pdo->prepare('SELECT * FROM users WHERE email = :email');$stmt->execute(['email' => $_GET['email']]);$user = $stmt->fetch();
# Pythoncursor.execute("SELECT * FROM users WHERE email = %s", (email,))

ORM như Eloquent, Django ORM thường tham số hóa sẵn, nhưng hãy cẩn thận với các hàm "raw" khi bạn ghép chuỗi. Với tên cột hoặc hướng sắp xếp do người dùng chọn, không thể tham số hóa, nên hãy so với danh sách cho phép cố định. Ngoài ra, cấp cho tài khoản database của ứng dụng quyền tối thiểu cần thiết để hạn chế thiệt hại.

XSS (Cross-Site Scripting)

XSS xảy ra khi ứng dụng hiển thị dữ liệu người dùng lên trang mà không thoát ký tự, cho phép kẻ tấn công chèn JavaScript chạy trong trình duyệt của nạn nhân và đánh cắp phiên, thao túng giao diện.

<!-- Nếu name = <script>alert(1)</script> --><p>Xin chào, <?= $name ?></p>   <!-- nguy hiểm khi không thoát --><p>Xin chào, <?= htmlspecialchars($name, ENT_QUOTES, 'UTF-8') ?></p>

Cách phòng chống:

  • Thoát đầu ra theo ngữ cảnh: các template engine hiện đại (Blade {{ }}, Twig, Jinja2, React) thoát mặc định. Hãy cảnh giác với các cú pháp "không thoát" như {!! !!} hay dangerouslySetInnerHTML.
  • Làm sạch HTML nếu thật sự cho phép người dùng nhập HTML, bằng thư viện chuyên dụng với danh sách thẻ cho phép.
  • Content Security Policy (CSP): header hạn chế nguồn script được chạy, thêm một lớp phòng thủ.
  • Cookie HttpOnly: JavaScript không đọc được cookie phiên.

CSRF (Cross-Site Request Forgery)

CSRF lợi dụng việc trình duyệt tự gửi cookie. Giả sử bạn đang đăng nhập ngân hàng, rồi mở một trang xấu chứa form tự gửi yêu cầu chuyển tiền tới ngân hàng; trình duyệt kèm cookie đăng nhập của bạn, và server tin đó là yêu cầu hợp lệ.

Cách phòng chống:

  • CSRF token: mỗi form chứa một token bí mật theo phiên mà trang bên ngoài không đoán được. Laravel, Django và nhiều framework có sẵn cơ chế này (ví dụ @csrf trong Blade).
  • Cookie SameSite: đặt SameSite=Lax hoặc Strict để trình duyệt không gửi cookie trong các yêu cầu chéo trang.
  • Đúng phương thức HTTP: không thay đổi dữ liệu bằng GET.
  • API dùng token trong header Authorization thay vì cookie thì ít bị CSRF hơn, nhưng đổi lại cần chăm chút chuyện lưu token.

Nguyên tắc chung

  1. Không tin dữ liệu từ client: kiểm tra và làm sạch ở server.
  2. Thoát đầu ra theo đúng ngữ cảnh: HTML, thuộc tính, URL, JavaScript, SQL đều có quy tắc riêng.
  3. Quyền tối thiểu: tài khoản database và dịch vụ chỉ có quyền cần thiết.
  4. Cập nhật thường xuyên: framework và thư viện được vá lỗi bảo mật.
  5. Thêm các header bảo mật và bật HTTPS toàn site.

Luyện tập trong môi trường an toàn

Cách học bảo mật hiệu quả là tự thử tấn công và phòng thủ trên ứng dụng của chính mình, chạy ở máy cá nhân. Hãy viết cố tình một form tìm kiếm dễ bị SQL Injection, thử nhập chuỗi đặc biệt, quan sát câu SQL được sinh ra, rồi sửa bằng prepared statement và so sánh. Tương tự với XSS: thử hiển thị bình luận có thẻ script khi không thoát, sau đó bật thoát và xem kết quả. Chỉ thực hành trên hệ thống của bạn hoặc môi trường học tập được phép; tuyệt đối không thử trên trang web của người khác khi chưa được cho phép. Đây vừa là kỹ năng nghề nghiệp, vừa là chuẩn mực đạo đức cơ bản của người làm bảo mật.

Kết luận

Ba lỗ hổng này đã có cách phòng rõ ràng: tham số hóa truy vấn, thoát đầu ra, và dùng token CSRF kèm cookie SameSite. Nếu dùng framework hiện đại đúng cách, phần lớn đã được bảo vệ sẵn; việc của bạn là không vô tình vô hiệu hóa các cơ chế đó. Hãy rà lại dự án của mình xem có đoạn SQL ghép chuỗi hay đoạn xuất HTML không thoát nào không.

Nếu muốn luyện tập cùng bài tập thực tế, hãy ghé các khóa học tại codebem.com khi bạn sẵn sàng.

author_photo
Nguyễn Đình Cường

0 comment