Table of Contents

SQL Injection là gì?

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.

Phương pháp phòng chống SQL Injection

Prepared Statements (Tham số hóa truy vấn):

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:

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 Inputs:

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);

Input Validation:

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
}

Using ORM:

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();

Least Privilege Principle:

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.

Kiểm tra hệ thống xem có lỗi SQL Injection hay không?

Kiểm tra thủ công

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ụ:

Sử dụng công cụ tự động

SQLMap

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

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

Review mã nguồn

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]);

Kiểm tra việc escaping

Đả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.

Audit và Penetration Testing

Monitoring và Logging