SQL Injection là một lỗ hổng bảo mật phổ biến trong các ứng dụng web. Nó xảy ra khi người dùng có thể chèn hoặc “tiêm” mã SQL không hợp lệ vào các câu truy vấn SQL thông qua các đầu vào từ phía người dùng như form, URL, cookie, v.v. Điều này có thể dẫn đến việc truy cập trái phép vào cơ sở dữ liệu, lấy cắp dữ liệu, hoặc thậm chí là phá hoại dữ liệu.
Ví dụ về SQL Injection:
SELECT * FROM users WHERE username = '$username' AND password = '$password';
Nếu $username được nhập là admin' OR '1'='1, câu truy vấn sẽ trở thành:
SELECT * FROM users WHERE username = 'admin' OR '1'='1' AND password = '$password';
Điều này có nghĩa là truy vấn sẽ luôn trả về kết quả hợp lệ vì điều kiện 1'='1 luôn đúng.
Hoặc kẻ tấn công nhập vào admin' – làm username, câu truy vấn sẽ trở thành:
SELECT * FROM users WHERE username = 'admin' -- ' AND password = '$password';
Kết quả là điều kiện kiểm tra mật khẩu bị bỏ qua do dấu – biến phần còn lại của câu truy vấn thành comment, dẫn đến việc kẻ tấn công có thể đăng nhập mà không cần mật khẩu.
Prepared statements đảm bảo rằng các giá trị được truyền vào câu truy vấn dưới dạng tham số và không thể thay đổi cấu trúc của truy vấn.
PHP với PDO:
$stmt = $pdo->prepare('SELECT * FROM users WHERE username = :username AND password = :password'); $stmt->execute(['username' => $username, 'password' => $password]);
Stored procedures là các hàm được lưu trữ trong cơ sở dữ liệu và được gọi từ ứng dụng. Chúng giúp ngăn chặn SQL Injection bằng cách tách biệt dữ liệu và mã SQL.
CALL AuthenticateUser(:username, :password);
Escaping đầu vào giúp đảm bảo rằng các ký tự đặc biệt trong đầu vào được xử lý đúng cách và không thể thay đổi cấu trúc của truy vấn.
PHP với MySQLi:
$username = $mysqli->real_escape_string($username);
Kiểm tra và xác thực đầu vào từ phía người dùng để đảm bảo rằng chúng tuân theo các quy tắc nhất định (ví dụ: chỉ chứa chữ cái và số).
if (preg_match("/^[a-zA-Z0-9]*$/", $username)) { // valid input }
Sử dụng các công cụ ORM (Object-Relational Mapping) như Eloquent trong Laravel hoặc Hibernate trong Java. Các công cụ này thường có cơ chế bảo vệ SQL Injection sẵn có.
Laravel Eloquent:
$user = User::where('username', $username)->first();
Chỉ cấp quyền hạn tối thiểu cần thiết cho các tài khoản truy cập cơ sở dữ liệu. Tài khoản truy cập từ ứng dụng không nên có quyền xóa hoặc thay đổi cấu trúc cơ sở dữ liệu.
Dùng các payloads cơ bản, nhập các chuỗi phổ biến vào các trường đầu vào và quan sát kết quả:
Ví dụ, trong trường nhập tên người dùng, bạn có thể thử nhập admin' – và xem kết quả. Nếu hệ thống trả về kết quả bất thường hoặc cho phép bạn đăng nhập mà không cần mật khẩu, có thể hệ thống dễ bị SQL Injection.
Quan sát các thông báo lỗi: Khi nhập các chuỗi không hợp lệ, nếu hệ thống hiển thị các thông báo lỗi SQL, có thể hệ thống dễ bị SQL Injection. Ví dụ:
SQLMap là một công cụ mã nguồn mở tự động phát hiện và khai thác lỗi SQL Injection.
Burp Suite là một công cụ kiểm tra bảo mật web phổ biến. Nó có thể tìm kiếm các lỗ hổng SQL Injection và nhiều loại lỗ hổng khác.
Kiểm tra mã nguồn để đảm bảo rằng tất cả các câu truy vấn SQL đều sử dụng prepared statements hoặc parameterized queries.
Ví dụ, thay vì:
$query = "SELECT * FROM users WHERE username = '$username' AND password = '$password'";
Hãy sử dụng:
$stmt = $pdo->prepare('SELECT * FROM users WHERE username = :username AND password = :password'); $stmt->execute(['username' => $username, 'password' => $password]);
Đảm bảo rằng các đầu vào từ người dùng được escaping một cách thích hợp. Ví dụ, trong PHP sử dụng hàm mysqli_real_escape_string hoặc tương đương.