SQL injection
Nội dung bài · 5 mục
- 1.Khái niệm
- 2.Ví dụ
- 3.Thử ngay
- 4.Lỗi hay gặp
- 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 quasetString(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.
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âuCâu SQL nào an toàn trước SQL injection?
Ô 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?
Ngoài chặn SQL injection, bind variable còn giúp gì?