第一篇 SQL注入
安全配置和編程安全並不是萬全之法,攻擊者往往可以通過對漏洞的試探找到新的突破口,甚至0days。
下面總結以下常見漏洞,在日常開發維護工作中可以留意。
*聊聊老朋友:SQL注入漏洞
多年前我還在念本科三年級的時候就做了一次關於SQL注入的攻防實驗,SQL注入在WEB1.0時代是非常常見的攻擊手段,特別是一些遠古時期的ASP、PHP站點,往往容易被注入提權。
簡單來說SQL注入就是利用url中參數請求,不斷嘗試獲取數據庫信息,從猜字段,推測表,甚至到最后暴庫得到后台管理員賬號密碼。
常見類型
A.報錯注入
假設可以通過url來傳遞該參數,如:
https://learnhackerphp.com/search?username=freephp
如本來的sql為:
select * from users where name = 'freephp'
但是我們在瀏覽器瀏覽框中輸入:
則sql語句變為:
select * from users where name='frephp'lol'
這會導致sql語句執行報錯,如果我們開啟了錯誤調試,可能會把數據庫的錯誤堆棧打印到瀏覽器頁面,這會被別有用心之輩利用。網站上線后一定設置display_errors=Off。
B.普通注入
例子如下:
https://localhost/search.php?name=name' OR 'a'='a
最終SQL為:
select * from user whre name=' name' OR 'a' ='a'
這變成了一個萬能查詢語句,可以查到你任何想要的數據,利用union和復合語句,甚至可以獲取到數據庫任何數據。
C.隱式類型注入
先了解一下MySQL默認的查詢優化器對入參的處理:
輸入類型 | 表字段類型 | 轉換后的類型 |
---|---|---|
NULL | 任意類型 | NULL |
STRING | STRING | STRING |
INT | INT | INT |
INT | STRING | DOUBLE |
INT | DOUBLE | DOUBLE |
INT | TIMESTAMP | TIMESTAMP |
任意類型 | DECIMAL | DECIMAL |
任意類型 | 十六進制 | 二進制 |
編寫如下sql:
select * from user whre address=0
可以獲取到該表的所有數據
D.無套路方式試探
比如在SQL語句當中附加一些其他執行命令,如:
select * from user where if (MID(version(), 1, 1) LIKE 5, sleep(5), 1)
如果真的讓MySQL查詢sleep了5秒,說明MySQL版本為5.
講完了常見SQL注入,那么如何來防范呢。其實PHP已經提供了一些優秀的預處理。
可以使用PDO或者mysqli*系列函數,對sql語句進行預編譯,杜絕sql注入。
<?php
require_once('../conf/db.php');
$pdo = new PDO($dns, $user, $password);
// ... some logic codes
$sql = 'insert into user (name,address) values(:name,:address)';
$stmt = $pdo->prepare($sql);
$name = "freephp'hack";
$address= "CDC,china";
// 綁定參數
$stmt->bindParam(':name', $name);
$stmt->bindParam(':address', $address);
$stmt->execute();
if ($stmt->errorCode() == 0) {
echo "insert success";
} else {
print_r($stmt->errorInfo());
}
然而在默認情況下,使用PDO也不是讓MySQL執行真正的預處理語句,一定要添加如下代碼:
$pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO:ERRMODE_EXCEPTION);
另外我們也應該對於用戶的入參進行判斷,比如有效性判斷、類型判斷,甚至加入一些有效類型數組來約束。
不要相信任何來自用戶的數據,永遠都留有一個悲觀鎖,即使你是一個命中注定的樂天派。