Trong các loại tấn công tiêm mã (injection), SQL Injection trong PHP nổi bật nhất. Hầu hết trang web đều dễ bị tấn công. Nghiên cứu cho thấy hơn 50% tấn công web sử dụng SQL Injection. Bài viết này giải thích cách tấn công diễn ra và cách ngăn chặn trong ứng dụng PHP.
SQL Injection (SQLi) là gì?
SQL Injection (SQLi) là kỹ thuật hacker thay đổi câu lệnh SQL chạy ở backend bằng cách làm giả các lệnh SQL được thực hiện. Tấn công diễn ra qua các trường nhập liệu của form, gây hậu quả nghiêm trọng cho cơ sở dữ liệu, dẫn đến rò rỉ thông tin nhạy cảm. Khi kiểm soát được database, hacker có thể chèn, cập nhật hoặc xóa dữ liệu.
Tấn công xảy ra khi code không được viết an toàn. Các lỗi thường gặp:
- Lọc ký tự khoảng trắng và xử lý kiểu dữ liệu không đúng
- Truyền dữ liệu chưa được làm sạch, mã hóa không đúng vào database
- Trộn lẫn code và dữ liệu
- Sử dụng dấu ngoặc đơn/ kép để phân tách chuỗi
Khi đầu vào người dùng không được kiểm tra đúng, database sẽ hiểu sai thành mã độc. Nếu đầu vào trực tiếp từ frontend được chèn vào truy vấn, sẽ dẫn đến xâm phạm.
Vấn đề khác: hiển thị lỗi cho người dùng - kẻ xấu có thể lợi dụng để biết loại và phiên bản database, từ đó triển khai exploit.
SQL Injection trong PHP là gì?
Ví dụ code không an toàn
Code PHP dễ bị tấn công sau nhận đầu vào người dùng trực tiếp không kiểm tra:
<?php
$username = $_POST['uname'];
$pass = $_POST['password'];
$sql = "SELECT * FROM user_table WHERE username='".$username."' AND userpwd='".$pass"';";
?>
Hacker có thể nhập giá trị như ' OR '1'='1 cho biến $password, khiến điều kiện luôn đúng (1=1), dẫn đến đăng nhập thành công mà không cần mật khẩu.
Ví dụ hiển thị lỗi ra màn hình (không an toàn):
<?php
...
elseif($mysqli->error) {
print($mysqli->error);
}
?>
Thay vì in lỗi ra màn hình, nên ghi log lỗi vào database/file riêng để theo dõi số lần bị tấn công SQLi.
Ví dụ code an toàn
Cách phòng chống: kiểm tra đầu vào trước khi thêm vào truy vấn SQL.
<?php
function checkUserInput($str) {
return str_replace(array("'","\"", "'", '''), array("'",""","'","""), $str);
}
$username = checkUserInput($_POST['uname']);
$pass = checkUserInput($_POST['password']);
?>
Phương pháp hiệu quả hơn: sử dụng Prepared Statements (PDO - PHP Data Objects). PDO cho phép truy vấn tham số hóa (parameterized queries), dễ đọc, dễ chuyển đổi, hỗ trợ nhiều loại database (không chỉ MySQL). PDO xác định phần nào là đầu vào người dùng, phần nào là lệnh database.
Ví dụ PDO cho MySQL:
// Thông tin DB
define('DB_HOST','localhost');
define('DB_USER','John_Reaper');
define('DB_PASS','admin@wsxdn.com@$$w0rd$');
define('DB_NAME','users');
if(isset($_GET['id'])) {
$id = $_GET['id'];
if(is_numeric($id) == true) {
try {
$dbh = new PDO('DBHOST;DB_NAME','DB_USER','DB_PASS');
$dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$q = "SELECT username FROM users WHERE id = :id";
$sth = $dbh->prepare($q);
$sth->bindParam(':id',$id);
$sth->execute();
$sth->setFetchMode(PDO::FETCH_ASSOC);
$res = $sth->fetchColumn();
print(htmlentities($res));
$dbh = null;
} catch(PDOException $e) {
error_log('PDOException - '.$e->getMessage(),0);
http_response_code(500);
die('Kết nối database thất bại');
}
} else {
http_response_code(400);
die('Yêu cầu không hợp lệ');
}
}
Cách ngăn chặn SQL Injection trong PHP
Áp dụng các biện pháp tốt nhất sau:
- Kiểm tra đầu vào người dùng ở frontend (JavaScript) hoặc backend (PHP).
- Không hiển thị lỗi cho người dùng. Ghi log lỗi vào file không truy cập được. Thông báo lỗi hiển thị phải chung chung, không tiết lộ thông tin database.
- Sử dụng truy vấn tham số hóa (parameterized query) - chỉ định phần SQL được coi là đầu vào người dùng.
- Xóa stored procedures không sử dụng - dễ bị khai thác.
- Phân quyền恰当 cho người dùng - hạn chế thiệt hại nếu hacker vượt qua xác thực.
Bảo vệ tuyệt đối khỏi PHP SQL Injection
Sử dụng tường lửa như Astra tăng cường miễn dịch với SQLi. Astra Security Suite bảo vệ khỏi 100+ tấn công khác: bad bots, XSS, CSRF...
Malware Scanner của Astra quét web dưới 10 phút (lần đầu) và <1 phút cho các lần sau. Dịch vụ Security Audit & Pentesting: kỹ sư quét thủ công từng dòng code để tìm và vá lỗ hổng, làm cho bảo mật impenetrable (không thể xâm nhập).
Thử demo Astra ngay hôm nay!