VieTopik
Tiếng HànTiếng Anh.NET (C#)Java
  • Góc học tập
Tải app
  • Thư viện
  • Luyện thi
  • Cẩm nang
  • Góc học tập
SQL với Oracle (Java)Hiệu năng và an toàn
Bài 16/16
5 phút

SQL injection

Nội dung bài · 5 mục
  1. 1.Khái niệm
  2. 2.Ví dụ
  3. 3.Thử ngay
  4. 4.Lỗi hay gặp
  5. 5.Tóm tắt

Trang tra cứu đơn hàng cho khách nhập email. Code ghép chuỗi email đó vào câu SQL rồi chạy. Một người gõ vào ô email một đoạn SQL thay vì email, và thế là họ đọc được thông tin của mọi khách. Đó là SQL injection, một lỗi bảo mật rất phổ biến.

Khái niệm

🧨 SQL injection: kiểu tấn công chèn đoạn SQL vào dữ liệu người dùng nhập, khiến câu lệnh chạy khác ý định ban đầu.

🧷 Bind variable (tham số): placeholder trong câu SQL như dấu ?, giá trị được gửi riêng nên Oracle luôn coi là dữ liệu, không bao giờ coi là code SQL.

Ví dụ

Code Java ghép chuỗi:

// SAI — ghép thẳng dữ liệu người dùng vào SQL
String sql =
    "SELECT name FROM customers WHERE email = '"
    + email + "'";

Viết bằng "... '%s'".formatted(email) (bài String của khoá Java Core) cũng là ghép chuỗi, nguy hiểm y hệt. Người dùng nhập ' OR '1'='1, câu SQL thành:

-- SAI — điều kiện luôn đúng
SELECT name FROM customers
WHERE email = '' OR '1'='1';

Dùng bind variable với PreparedStatement của JDBC, bộ thư viện có sẵn trong Java để làm việc với database:

// ĐÚNG — email được gửi riêng, không ghép vào SQL
import java.sql.*;

void main() throws SQLException {
    String email = "an@shop.vn"; // người dùng nhập
    var conn = DriverManager.getConnection(
        "jdbc:oracle:thin:@localhost:1521/FREEPDB1",
        "shop", "shop_pw");
    var ps = conn.prepareStatement(
        "SELECT name FROM customers WHERE email = ?");
    ps.setString(1, email);
}

import java.sql.*; lấy các class của JDBC; conn là kết nối tới Oracle; SQLException là checked exception nên main phải ghi throws. Đoạn này chỉ để đọc hiểu; muốn chạy thật thì cần thêm driver Oracle JDBC (ojdbc11).

  • ? là placeholder, giá trị thật truyền qua setString(1, email): số 1 là placeholder thứ nhất.
  • Người dùng gõ gì vào ô email thì Oracle cũng chỉ coi đó là một chuỗi để so sánh.
  • Bind variable còn giúp Oracle dùng lại cách chạy đã tính cho câu SQL đó khi chỉ có giá trị thay đổi, nên nhanh hơn.
Người dùng nhập ' OR '1'='1 Ghép chuỗi Bind variable Đoạn nhập thành một phần code SQL, WHERE bị đổi nghĩa Câu SQL giữ nguyên, đoạn nhập đi riêng và chỉ là chuỗi để so với email
Cùng một đoạn nhập vào, hai cách đưa vào SQL

Thử ngay

Chạy thử câu SQL mà kẻ tấn công tạo ra:

SELECT name
FROM customers
WHERE email = '' OR '1'='1'
ORDER BY customer_id;

Đoán trước khi chạy: email = '' không khớp dòng nào (bài NULL). Câu này trả về mấy khách?

Xem kết quả
NAME
An
Bình
Chi
Dũng

Cả bốn khách. '1'='1' luôn đúng, nối bằng OR nên điều kiện đúng với mọi dòng. Thông tin của mọi khách bị lộ chỉ vì một ô nhập liệu.

Lỗi hay gặp

Ghép chuỗi vì "dữ liệu này an toàn". Hôm nay giá trị đến từ code, mai có người đổi thành lấy từ ô nhập liệu. Luôn dùng bind variable cho mọi giá trị đưa vào SQL.

Tự lọc dấu nháy thay vì dùng tham số. Tự thay ' bằng '' chặn được ví dụ trên, nhưng dễ quên ở một chỗ nào đó, và không giúp gì khi giá trị ghép vào không nằm trong nháy, như một con số.

// SAI — tự "làm sạch" vẫn là ghép chuỗi
String safe = email.replace("'", "''");
String sql =
    "SELECT name FROM customers WHERE email = '"
    + safe + "'";

Dùng bind variable như ở mục Ví dụ: dữ liệu tách hẳn khỏi code SQL, nên không cần tự lọc gì.

Tóm tắt

  • SQL injection xảy ra khi dữ liệu người dùng được ghép thẳng vào câu SQL.
  • Kẻ tấn công chèn đoạn như ' OR '1'='1 để đổi ý nghĩa câu lệnh.
  • Dùng bind variable (?) cho mọi giá trị, không tự ghép chuỗi.
  • Tự lọc dấu nháy không thay được bind variable.

Tự kiểm tra

0/3 câu
Câu 1

Câu SQL nào an toàn trước SQL injection?

Câu 2

Ô tìm kiếm ghép thẳng vào SQL. Người dùng nhập ' OR '1'='1. Chuyện gì xảy ra?

Câu 3

Ngoài chặn SQL injection, bind variable còn giúp gì?

Chuẩn hoá

Nội dung bài

  1. 1.Khái niệm
  2. 2.Ví dụ
  3. 3.Thử ngay
  4. 4.Lỗi hay gặp
  5. 5.Tóm tắt