為什么會存在SQL注入
通俗來講艇拍,sql作為一種解釋型語言,在運(yùn)行時是由一個運(yùn)行時組件解釋語言代碼并執(zhí)行其中包含的指令的語言宠纯⌒断Γ基于這種執(zhí)行方式,產(chǎn)生了一系列叫做代碼注入(code injection)的漏洞 婆瓜。它的數(shù)據(jù)其實(shí)是由程序員編寫的代碼和用戶提交的數(shù)據(jù)共同組成的快集。程序員在web開發(fā)時贡羔,沒有過濾敏感字符,綁定變量个初,導(dǎo)致攻擊者可以通過sql靈活多變的語法乖寒,構(gòu)造精心巧妙的語句,不擇手段院溺,達(dá)成目的楣嘁,或者通過系統(tǒng)報錯,返回對自己有用的信息珍逸。
經(jīng)常我們在面試中會遇到"sql中#和$的區(qū)別"逐虚,都會提到注入問題:
(1)#將傳入的數(shù)據(jù)都當(dāng)成一個字符串,會對自動傳入的數(shù)據(jù)加一個雙引號谆膳。如:order by #user_id#叭爱,如果傳入的值是id,則解析成的sql為order by "id"漱病。
(2)$將傳入的數(shù)據(jù)直接顯示生成在sql中买雾。如:order by $user_id$,如果傳入的值是id缨称,則解析成的sql為order by id凝果。
(3)#方式在很大程度上能夠防止sql注入。
(4)$方式無法防止sql注入睦尽。
SQL注入演示
以PHP+MySQL為例器净,以一個Web網(wǎng)站中最基本的用戶系統(tǒng)來做實(shí)例演示。
1.創(chuàng)建一個名為demo的數(shù)據(jù)庫:
CREATE DATABASE `demo` DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci;
2.創(chuàng)建一個名為user的數(shù)據(jù)表当凡,并插入1條演示數(shù)據(jù):
CREATE TABLE `demo`.`user` (
`uid` INT( 11 ) NOT NULL AUTO_INCREMENT PRIMARY KEY COMMENT '用戶uid',
`username` VARCHAR( 20 ) NOT NULL COMMENT '用戶名',
`password` VARCHAR( 32 ) NOT NULL COMMENT '用戶密碼'
) ENGINE = INNODB;
INSERT INTO `demo`.`user` (`uid`, `username`, `password`) VALUES ('1', 'plhwin', MD5('123456'));
實(shí)例一
傳入username參數(shù)山害,在頁面打印出這個user的詳細(xì)信息,編寫 userinfo.php 程序代碼:
<?php
header('Content-type:text/html; charset=UTF-8');
$username = isset($_GET['username']) ? $_GET['username'] : '';
$userinfo = array();
if($username){
//使用mysqli驅(qū)動連接demo數(shù)據(jù)庫
$mysqli = new mysqli("localhost", "root", "root", 'demo');
$sql = "SELECT uid,username FROM user WHERE username='{$username}'";
//mysqli multi_query 支持執(zhí)行多條MySQL語句
$query = $mysqli->multi_query($sql);
if($query){
do {
$result = $mysqli->store_result();
while($row = $result->fetch_assoc()){
$userinfo[] = $row;
}
if(!$mysqli->more_results()){
break;
}
} while ($mysqli->next_result());
}
}
echo '<pre>',print_r($userinfo, 1),'</pre>';
上面這個程序要實(shí)現(xiàn)的功能是根據(jù)瀏覽器傳入的用戶名參數(shù)沿量,在頁面上打印出這個用戶的詳細(xì)信息浪慌,采用了mysqli的驅(qū)動,以便能使用到 multi_query 方法來支持同時執(zhí)行多條SQL語句朴则,這樣能更好的說明SQL注入攻擊的危害性权纤。
假設(shè)我們可以通過 http://localhost/test/userinfo.php?username=plhwin
這個URL來訪問到具體某個會員的詳情,正常情況下乌妒,如果瀏覽器里傳入的username是合法的汹想,那么SQL語句會執(zhí)行:
SELECT uid,username FROM user WHERE username='plhwin'
如果在瀏覽器里把傳入的username參數(shù)變?yōu)?"plhwin';SHOW TABLES-- hack",也就是當(dāng)URL變?yōu)?code>http://localhost/test/userinfo.php?username=plhwin';SHOW TABLES-- hack的時候撤蚊,此時我們程序?qū)嶋H執(zhí)行的SQL語句變成了:
SELECT uid,username FROM user WHERE username='plhwin';SHOW TABLES-- hack'
--會注釋后面的內(nèi)容
此時可以在瀏覽器里看到頁面的輸出,數(shù)據(jù)庫的user表古掏,此時將參數(shù)換成plhwin';DROP TABLE user-- hack
,那將產(chǎn)生災(zāi)難性的嚴(yán)重結(jié)果侦啸。
Array
(
[0] => Array
(
[uid] => 1
[username] => plhwin
)
[1] => Array
(
[Tables_in_demo] => user
)
)
SQL注入攻擊的總體思路
1.尋找到SQL注入的位置
2.判斷服務(wù)器類型和后臺數(shù)據(jù)庫類型
3.針對不通的服務(wù)器和數(shù)據(jù)庫特點(diǎn)進(jìn)行SQL注入攻擊
主要講一下如何尋找SQL注入點(diǎn)
尋找注入點(diǎn)常用的方法就是通過 不斷調(diào)整參數(shù)來測試服務(wù)器的響應(yīng)槽唾,進(jìn)而暴露出與數(shù)據(jù)庫結(jié)構(gòu)有關(guān)的問題丧枪。傳入SQL語句可控參數(shù)分為兩類
- 數(shù)字類型,參數(shù)不用被引號括起來庞萍,如
?id=1
- 其他類型拧烦,參數(shù)要被引號擴(kuò)起來,如
?name="phone"
數(shù)字類型
構(gòu)造測試 | 預(yù)期結(jié)果 | 變種 |
---|---|---|
' | 觸發(fā)錯誤,返回數(shù)據(jù)庫錯誤 | |
1+1 | 返回原來相同的結(jié)果 | 3-1 |
1+0 | 返回原來相同的結(jié)果 | |
1 or 1=1 | 永真條件挂绰,返回所有記錄 | 1) or (1=1 |
1 or 1=2 | 空條件屎篱,返回原來相同的結(jié)果 | 1) or (1=2 |
1 and 1=2 | 永假條件,不返回記錄 | 1) and (1=2 |
其他類型
構(gòu)造測試 | 預(yù)期結(jié)果 | 變種 |
---|---|---|
col 3 is | right-aligned | $1600 |
col 2 is | centered | $12 |
zebra stripes | are neat | $1 |
a' | 觸發(fā)錯誤葵蒂,返回數(shù)據(jù)庫錯誤 | |
a' or '1'='1 | 永真條件交播,返回所有記錄 | a') or ('1'=1 |
a' or '1'='2 | 空條件,返回原來相同結(jié)果 | a') or ('1'=2 |
a' and '1'='2 | 永假條件践付,不返回記錄 | a') and ('1'='2 |
常見基本的方法有如下幾種:
1.加引號法,通過在瀏覽器地址欄中的頁面鏈接地址后面增加一個單引號秦士,進(jìn)而測試服務(wù)器的響應(yīng),來判斷是否存在注入點(diǎn)永高。
2.“1=1和1=2”法
后續(xù)待補(bǔ)充
如何預(yù)防SQL注入
1. PreparedStatement預(yù)編譯語句(簡單最有效)
采用預(yù)編譯語句集隧土,它內(nèi)置了處理SQL注入的能力,不同的程序語言命爬,都分別有使用預(yù)編譯語句的方法曹傀。
使用好處:
(1).代碼的可讀性和可維護(hù)性.
(2).PreparedStatement盡最大可能提高性能.
(3).最重要的一點(diǎn)是極大地提高了安全性.
還是以php為例:
<?php
header('Content-type:text/html; charset=UTF-8');
$username = isset($_GET['username']) ? $_GET['username'] : '';
$userinfo = array();
if($username){
//使用mysqli驅(qū)動連接demo數(shù)據(jù)庫
$mysqli = new mysqli("localhost", "root", "root", 'demo');
//使用問號替代變量位置
$sql = "SELECT uid,username FROM user WHERE username=?";
$stmt = $mysqli->prepare($sql);
//綁定變量
$stmt->bind_param("s", $username);
$stmt->execute();
$stmt->bind_result($uid, $username);
while ($stmt->fetch()) {
$row = array();
$row['uid'] = $uid;
$row['username'] = $username;
$userinfo[] = $row;
}
}
echo '<pre>',print_r($userinfo, 1),'</pre>';
我們程序里并沒有使用addslashes函數(shù),但是瀏覽器里運(yùn)行 http://localhost/test/userinfo2.php?username=plhwin' AND 1=1-- hack
里得不到任何結(jié)果饲宛,說明SQL漏洞在這個程序里并不存在皆愉。實(shí)際上,綁定變量使用預(yù)編譯語句是預(yù)防SQL注入的最佳方式艇抠,使用預(yù)編譯的SQL語句語義不會發(fā)生改變幕庐,在SQL語句中,變量用問號?表示家淤,黑客即使本事再大异剥,也無法改變SQL語句的結(jié)構(gòu),像上面例子中絮重,username變量傳遞的 plhwin' AND 1=1-- hack 參數(shù)冤寿,也只會當(dāng)作username字符串來解釋查詢,從根本上杜絕了SQL注入青伤。
2.用正則表達(dá)式過濾傳入的參數(shù)
如關(guān)鍵字過濾:
reg = "(?:')|(?:--)|(/\\*(?:.|[\\n\\r])*?\\*/)|" + "(\\b(select|update|and|or|delete|insert|trancate|char|into|substr|ascii|declare|exec|count|master|into|drop|execute)\\b)";
3.字符串過濾
4.js代碼預(yù)防注入(后臺仍然需要預(yù)防)
5.不要隨意開啟生產(chǎn)環(huán)境中Webserver的錯誤顯示督怜,同時不要信任任何用戶輸入的數(shù)據(jù),對請求服務(wù)器的數(shù)據(jù)需要進(jìn)行一定的規(guī)則和格式校驗(yàn)潮模。
附sql注入系列專題:sql注入專題
同時推薦testfire.net來模擬漏洞攻擊:demo.testfire.net
看不到我 / \ 看不到我~~~~