Khi nói đến các truy vấn cơ sở dữ liệu, hãy luôn thử và sử dụng các truy vấn được tham số sẵn. Các mysqli
và PDO
các thư viện hỗ trợ này. Điều này vô cùng an toàn hơn so với việc sử dụng các chức năng thoát như mysql_real_escape_string
.
Vâng, mysql_real_escape_string
thực sự chỉ là một hàm thoát chuỗi. Nó không phải là một viên đạn ma thuật. Tất cả những gì nó sẽ làm là thoát các ký tự nguy hiểm để chúng có thể an toàn khi sử dụng trong một chuỗi truy vấn duy nhất. Tuy nhiên, nếu bạn không làm sạch đầu vào của mình trước thì bạn sẽ dễ bị tấn công bởi một số vectơ tấn công.
Hãy tưởng tượng câu lệnh SQL sau:
$result = "SELECT fields FROM table WHERE id = ".mysql_real_escape_string($_POST['id']);
Bạn có thể thấy rằng điều này rất dễ bị khai thác.
Hãy tưởng tượng id
tham số chứa vector tấn công phổ biến:
1 OR 1=1
Không có ký tự rủi ro nào trong đó để mã hóa, vì vậy nó sẽ đi thẳng qua bộ lọc thoát. Để lại cho chúng tôi:
SELECT fields FROM table WHERE id= 1 OR 1=1
Đây là một vectơ SQL injection đáng yêu và sẽ cho phép kẻ tấn công trả lại tất cả các hàng. Hoặc là
1 or is_admin=1 order by id limit 1
sản xuất
SELECT fields FROM table WHERE id=1 or is_admin=1 order by id limit 1
Điều này cho phép kẻ tấn công trả lại thông tin chi tiết của quản trị viên đầu tiên trong ví dụ hoàn toàn hư cấu này.
Mặc dù các chức năng này hữu ích nhưng chúng phải được sử dụng cẩn thận. Bạn cần đảm bảo rằng tất cả các đầu vào web đều được xác thực ở một mức độ nào đó. Trong trường hợp này, chúng tôi thấy rằng chúng tôi có thể bị khai thác vì chúng tôi đã không kiểm tra xem biến chúng tôi đang sử dụng dưới dạng số có thực sự là số hay không. Trong PHP, bạn nên sử dụng rộng rãi một tập hợp các hàm để kiểm tra xem đầu vào có phải là số nguyên, float, chữ và số, v.v. Nhưng khi nói đến SQL, hầu hết đều chú ý đến giá trị của câu lệnh đã chuẩn bị. Đoạn mã trên sẽ được bảo mật nếu nó là một câu lệnh được chuẩn bị sẵn vì các hàm cơ sở dữ liệu sẽ biết rằng đó 1 OR 1=1
không phải là một chữ hợp lệ.
Đối với htmlspecialchars()
. Đó là một bãi mìn của riêng nó.
Có một vấn đề thực sự trong PHP là nó có toàn bộ lựa chọn các hàm thoát khác nhau liên quan đến html và không có hướng dẫn rõ ràng về chính xác hàm nào làm những gì.
Thứ nhất, nếu bạn đang ở bên trong thẻ HTML, bạn đang gặp rắc rối thực sự. Nhìn vào
echo '<img src= "' . htmlspecialchars($_GET['imagesrc']) . '" />';
Chúng ta đã ở trong thẻ HTML, vì vậy chúng ta không cần <hoặc> làm bất cứ điều gì nguy hiểm. Véc tơ tấn công của chúng tôi có thể làjavascript:alert(document.cookie)
Bây giờ HTML kết quả trông giống như
<img src= "javascript:alert(document.cookie)" />
Cuộc tấn công được thông qua.
Nó trở nên tồi tệ hơn. Tại sao? bởi vì htmlspecialchars
(khi được gọi theo cách này) chỉ mã hóa dấu ngoặc kép chứ không mã hóa đơn. Vì vậy, nếu chúng ta có
echo "<img src= '" . htmlspecialchars($_GET['imagesrc']) . ". />";
Kẻ tấn công xấu xa của chúng ta giờ có thể đưa các thông số hoàn toàn mới vào
pic.png' onclick='location.href=xxx' onmouseover='...
cho chúng tôi
<img src='pic.png' onclick='location.href=xxx' onmouseover='...' />
Trong những trường hợp này, không có viên đạn ma thuật nào cả, bạn chỉ cần tự đánh đầu vào. Nếu bạn cố gắng và lọc ra những ký tự xấu chắc chắn bạn sẽ thất bại. Thực hiện cách tiếp cận danh sách trắng và chỉ thông qua các ký tự tốt. Xem bảng gian lận XSS để biết các ví dụ về mức độ đa dạng của các vectơ
Ngay cả khi bạn sử dụng htmlspecialchars($string)
bên ngoài các thẻ HTML, bạn vẫn dễ bị tấn công bởi các vectơ tấn công bộ ký tự nhiều byte.
Hiệu quả nhất mà bạn có thể đạt được là sử dụng kết hợp mb_convert_encoding và htmlentities như sau.
$str = mb_convert_encoding($str, 'UTF-8', 'UTF-8');
$str = htmlentities($str, ENT_QUOTES, 'UTF-8');
Thậm chí, điều này còn khiến IE6 dễ bị tấn công, do cách nó xử lý UTF. Tuy nhiên, bạn có thể quay trở lại mã hóa hạn chế hơn, chẳng hạn như ISO-8859-1, cho đến khi việc sử dụng IE6 giảm xuống.
Để có nghiên cứu chuyên sâu hơn về các vấn đề multibyte, hãy xem https://stackoverflow.com/a/12118602/1820