在 Day 21 我們提到 SQL Injection 的概念,在 Day 22 我們來挖一個洞吧,畢竟知道怎麼挖洞才會意識到洞長什麼樣子嘛
首先,先做出一個超簡易的表單
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
</head>
<body>
<form method="post" action="test.php">
<label for="ID">帳號</label>
<input type="text" name="ID"><br>
<input type="submit" name="submit">
</form>
</body>
</html>
再來做出一個接收表單的 PHP
<?php
$UserID = $_POST["ID"];
$sql = "SELECT * FROM account WHERE UserID ='$UserID' ";
$mysqli = mysqli_connect("localhost","root","","foo");
$result = mysqli_query( $mysqli,$sql );
if($result)
echo "哈囉!";
else
echo "這裡不歡迎你";
?>
這樣一個漏洞就做好囉
我們來試一下,先看一下資料庫有哪些資料
試著輸入其中一個 UserID 進表單中
會得到這樣的結果
如果今天的輸入是這樣呢
這樣也是可以通過檢查的喔
如果套到程式碼中會比較容易理解發生什麼事情,我們程式中的 SQL 語法長這樣SELECT * FROM account WHERE UserID ='$UserID'
把輸入的句子套到$UserID
變這樣SELECT * FROM account WHERE UserID ='1' OR '1' ='1'
WHERE 後面的條件恆成立,所以等於這樣SELECT * FROM account
如果這部分的查詢是可以撈出一些資料的話,那所有資料都被看光光囉
只要是使用者輸入的內容都應該額外檢查與防護,因此使用者輸入的值要藉由 binding 的方式填入比較安全。
<?php
$UserID = $_POST["ID"];
$sql = "SELECT * FROM account WHERE UserID =? ";
$db = new mysqli('127.0.0.1', 'root', '', 'foo');
$stmt = $db->prepare($sql);
$stmt->bind_param('s',$UserID,);
$stmt->execute();
if($stmt->affected_rows === 1)
echo "哈囉!$UserID";
else
echo "這裡不歡迎你";
?>
一樣輸入攻擊語句,這次就沒有效果了,因為 bind_param 不會把單引號和雙引號當成指令的一部份,而是要查詢的內容。