MySQL - và SQL Injection
Nếu bạn lấy thông tin đầu vào của người dùng thông qua một trang web và chèn nó vào cơ sở dữ liệu MySQL, có khả năng bạn đã bỏ ngỏ cho mình một vấn đề bảo mật được gọi là SQL Injection. Chương này sẽ dạy bạn cách ngăn điều này xảy ra và giúp bạn bảo mật các tập lệnh và câu lệnh MySQL của mình.
SQL Injection thường xảy ra khi bạn yêu cầu người dùng nhập vào, như tên của họ và thay vì tên họ cung cấp cho bạn một câu lệnh MySQL mà bạn sẽ vô tình chạy trên cơ sở dữ liệu của mình.
Không bao giờ tin tưởng vào dữ liệu do người dùng cung cấp, chỉ xử lý dữ liệu này sau khi xác nhận; như một quy luật, điều này được thực hiện bằng cách đối sánh mẫu. Trong ví dụ sau, tên người dùng bị giới hạn ở các ký tự chữ và số cộng với dấu gạch dưới và có độ dài từ 8 đến 20 ký tự - hãy sửa đổi các quy tắc này nếu cần.
if (preg_match("/^\w{8,20}$/", $_GET['username'], $matches)) {
$result = mysql_query("SELECT * FROM users WHERE username = $matches[0]");
} else {
echo "username not accepted";
}
Để chứng minh vấn đề này, hãy xem xét đoạn trích sau.
// supposed input
$name = "Qadir'; DELETE FROM users;";
mysql_query("SELECT * FROM users WHERE name = '{$name}'");
Lời gọi hàm phải lấy một bản ghi từ bảng người dùng, trong đó cột tên khớp với tên do người dùng chỉ định. Trong trường hợp bình thường, $ name sẽ chỉ chứa các ký tự chữ và số và có thể là khoảng trắng. Nhưng ở đây, bằng cách thêm một truy vấn hoàn toàn mới vào$name, cuộc gọi đến cơ sở dữ liệu biến thành một thảm họa. Truy vấn DELETE được đưa vào sẽ xóa tất cả các bản ghi khỏi người dùng.
May mắn thay, nếu bạn sử dụng MySQL, mysql_query()hàm không cho phép xếp chồng truy vấn hoặc thực hiện nhiều truy vấn trong một lệnh gọi hàm duy nhất. Nếu bạn cố gắng xếp chồng các truy vấn, cuộc gọi không thành công.
Tuy nhiên, các phần mở rộng cơ sở dữ liệu PHP khác, chẳng hạn như SQLite và PostgreSQL, vui vẻ thực hiện các truy vấn xếp chồng lên nhau, thực hiện tất cả các truy vấn được cung cấp trong một chuỗi và tạo ra một vấn đề bảo mật nghiêm trọng.
Ngăn chặn SQL Injection
Bạn có thể xử lý tất cả các ký tự thoát một cách thông minh bằng các ngôn ngữ lập trình như PERL và PHP. Phần mở rộng MySQL cho PHP cung cấp chức năngmysql_real_escape_string() để thoát các ký tự đầu vào đặc biệt đối với MySQL.
if (get_magic_quotes_gpc()) {
$name = stripslashes($name);
}
$name = mysql_real_escape_string($name);
mysql_query("SELECT * FROM users WHERE name = '{$name}'");
Các Quandary THÍCH
Để giải quyết vấn đề LIKE, cơ chế thoát tùy chỉnh phải chuyển đổi các ký tự% và _ do người dùng cung cấp thành các ký tự. Sử dụngaddcslashes(), một hàm cho phép bạn chỉ định một phạm vi ký tự để thoát.
$sub = addcslashes(mysql_real_escape_string("%something_"), "%_");
// $sub == \%something\_
mysql_query("SELECT * FROM messages WHERE subject LIKE '{$sub}%'");