1、注入攻擊種類
1. GET注入
輸入參數經過URL發送。
2. POST注入
輸入參數經過HTTP正文發送
3. COOKIE注入
輸入參數經過HTTP cookie發送
4. HTTP Headers注入
經過http提交應用程序時使用的頭
2、各類攻擊所佔比及防範程度
1. 漏洞發現佔比
從上圖中,能夠看到,大部分的http header和http cookie漏洞不能被發現。
2. 漏洞防範佔比
從上圖中,能夠得出,大部分的攻擊防範上,在於get和post,而對於cookie和header則不多,所以很容易在這方面失守。
3、注入攻擊實驗分析
1. 建立實驗數據庫,分兩種類型編碼:GBK/UTF8
$createSqlGbk = "CREATE TABLE `user` (`id` int(11) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '用戶ID' ,`username` varchar(32) NOT NULL COMMENT '用戶名' ,`password` varchar(32) NOT NULL ,`remark` text NULL ,PRIMARY KEY (`id`)) DEFAULT CHARACTER SET=gbk";
$createSqlUtf8 = "CREATE TABLE `user` (`id` int(11) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '用戶ID' ,`username` varchar(32) NOT NULL COMMENT '用戶名' ,`password` varchar(32) NOT NULL ,`remark` text NULL ,PRIMARY KEY (`id`)) DEFAULT CHARACTER SET=utf8";
其中對於多字節編碼的數據庫,數據在PHP層轉義時,GBK存在BUG,所以能夠進行測試對比。
2. 初始化數據庫
$insertDataSql = "INSERT INTO `user` VALUES ('1', 'test', '123456', null), ('2', 'guest', '888888', null), ('3', 'admin', '888888', null)";
其中構建一個簡單表,放幾行簡單的數據,用於測試。
3. 構建測試代碼
<
html>
<
head>
<
title>SQL注入攻擊與防範</
title>
<
meta http-equiv=
"Content-Type"
content=
"text/html; charset=UTF-8"/>
<
style>
body{
font-
size:
12px;margin:
20px;}
h3{
font-
size:
14px;margin:
20px
0;}
a{
font-
size:
12px;margin:
10px
20px;}
</
style>
</
head>
<
body>
<
h3>SQL注入攻擊(GBK)</
h3>
<
a
href=
"./inject.php?type=gbk&id=1">正常取值</
a>
<
a
href=
"./inject.php?type=gbk&id=2341 or 1 = 1 -- dasf">SQL注入(針對直接等於寫法)</
a>
<
a
href=
"./inject.php?type=gbk&id=2341' or 1 = 1 -- asdf">SQL注入(針對用引號包括寫法)</
a>
<
a
href=
"./inject.php?type=gbk&isslashes=1&id=2341<?php echo chr(0xbf) . chr(0x27); ?> or 1 = 1 -- asd">SQL注入(針對對特殊字符轉義寫法)</
a>
<
h3>SQL注入攻擊(UTF8)</
h3>
<
a
href=
"./inject.php?type=utf8&id=1">正常取值</
a>
<
a
href=
"./inject.php?type=utf8&id=2341 or 1 = 1 -- dasf">SQL注入(針對直接等於寫法)</
a>
<
a
href=
"./inject.php?type=utf8&id=2341' or 1 = 1 -- asdf">SQL注入(針對用引號包括寫法)</
a>
<
a
href=
"./inject.php?type=utf8&isslashes=1&id=2341<?php echo chr(0xbf) . chr(0x27); ?> or 1 = 1 -- asd">SQL注入(針對對特殊字符轉義寫法)</
a>
<
div
style=
"width:100%;height:5px;margin:10px 0px;clear:both;background-color:gray;"></
div>
<?php
if (isset($_GET['
type'])) {
$
type = $_GET['
type'];
} else {
exit();
}
echo
"<h1 style='font-size:12px;'>CHARSET:: <b style='color:red;font-size:18px;'>" . $
type .
"</b></h1>";
// 創建mysql鏈接
$c = mysql_connect(
"localhost",
"root",
"");
echo
"<h1 style='font-size:14px;color:red;'>SQL注入攻擊及防範</h1>";
if (isset($_GET['
id'])) {
if (isset($_GET['isslashes'])) {
$
id = addslashes($_GET['
id']);
} else {
$
id = $_GET['
id'];
}
// 三種sql寫法
$sql1 =
"SELECT * FROM user WHERE id = $id";
$sql2 =
"SELECT * FROM user WHERE id = '{$id}'";
$sql =
"SELECT * FROM user WHERE id = " . intval($
id);
}
//根據編碼不一樣,選擇鏈接對應編碼數據庫
if ($
type == 'gbk') {
mysql_select_db(
"testInjectionDb_gbk", $c);
// change our character set
mysql_query(
"SET CHARACTER SET 'gbk'", $c);
} else {
mysql_select_db(
"testInjectionDb_utf8", $c);
// change our character set
mysql_query(
"SET CHARACTER SET 'utf8'", $c);
}
// 依次執行三種寫法的查詢
echo
"<div style='font-size:12px;'>++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++</div>";
echo
"<h1 style='font-size:12px;'>被攻擊 SQL1:: <b style='color:red;font-size:14px;'>" . $sql1 .
"</b></h1>";
$r1 = mysql_query($sql1, $c);
if ($r1) {
echo
"<h1 style='font-size:12px;color:green;'>影響數據條數:: <b style='color:red;font-size:18px;'>" . mysql_num_rows($r1) .
"</b></h1>";
} else {
echo
"<h1 style='font-size:12px;color:red;'>語法錯誤!</h1>";
}
echo
"<div style='font-size:12px;'>++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++</div>";
echo
"<h1 style='font-size:12px;'>被攻擊 SQL2:: <b style='color:red;font-size:14px;'>" . $sql2 .
"</b></h1>";
$r2 = mysql_query($sql2, $c);
if ($r2) {
echo
"<h1 style='font-size:12px;color:green;'>影響數據條數:: <b style='color:red;font-size:18px;'>" . mysql_num_rows($r2) .
"</b></h1>";
} else {
echo
"<h1 style='font-size:12px;color:red;'>語法錯誤!</h1>";
}
echo
"<div style='font-size:12px;'>++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++</div>";
echo
"<h1 style='font-size:12px;'>強制整型較驗 SQL:: <b style='color:red;font-size:14px;'>" . $sql .
"</b></h1>";
$r = mysql_query($sql, $c);
if ($r) {
echo
"<h1 style='font-size:12px;color:green;'>影響數據條數:: <b style='color:red;font-size:18px;'>" . mysql_num_rows($r) .
"</b></h1>";
} else {
echo
"<h1 style='font-size:12px;color:red;'>語法錯誤!</h1>";
}
// 創建PDO的鏈接,萬能防注入。
// 注意:
//
1. 參數 PDO::ATTR_EMULATE_PREPARES 設置爲 false;
//
2. 你不能讓佔位符 ? 代替一組值,如:
SELECT * FROM blog WHERE userid IN ( ? );
//
3. 你不能讓佔位符代替數據表名或列名,如:
SELECT * FROM blog ORDER BY ?;
//
4. 你不能讓佔位符 ? 代替任何其餘SQL語法,如:
SELECT EXTRACT( ? FROM datetime_column) AS variable_datetime_element FROM blog;
echo
"<div style='font-size:12px;'>++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++</div>";
if ($
type == 'gbk') {
$pdo = new PDO(
"mysql:host=localhost;dbname=testInjectionDb_gbk",
"root",
"");
} else {
$pdo = new PDO(
"mysql:host=localhost;dbname=testInjectionDb_utf8",
"root",
"");
}
$pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, FALSE);
$st = $pdo->prepare(
"SELECT * FROM user WHERE id = ?");
$st->bindParam(
1, $
id);
$c = $st->execute();
echo
"<h1 style='font-size:12px;'>PDO防注入(萬能防注入) SQL:: <b style='color:red;font-size:14px;'>" . $st->queryString .
"</b></h1>";
if ($c) {
$rs = $st->fetchAll();
echo
"<h1 style='font-size:12px;color:green;'>影響數據條數:: <b style='color:red;font-size:18px;'>" . count($rs) .
"</b></h1>";
var_dump($rs);
$st->debugDumpParams();
} else {
echo
"<h1 style='font-size:12px;color:red;'>語法錯誤!</h1>";
}
?>
</
body>
</
html>
以上代碼構建了兩種編碼(gbk/utf8),執行邏輯分四種實現方式,各四種訪問方式,其中第一種爲正常訪問,後三種分別爲注入型寫法。
4. 四種實現寫法
$sql1 = "SELECT * FROM user WHERE id = $id";$sql2 = "SELECT * FROM user WHERE id = '{$id}'";$sql = "SELECT * FROM user WHERE id = " . intval($id);
$st = $pdo->prepare("SELECT * FROM user WHERE id = ?");$st->bindParam(1, $id);$c = $st->execute();
5. 應對攻擊的表現
<a href="./inject.php?type=gbk&id=2341 or 1 = 1 -- dasf">SQL注入(針對直接等於寫法)</a>
<a href="./inject.php?type=utf8&id=2341 or 1 = 1 -- dasf">SQL注入(針對直接等於寫法)</a>
從上面能夠很明確的看到,在所傳參數中,帶有注入性語句。表現結果:
GBK編碼:
UTF8編碼:
從中能夠看到,兩種編碼的數據庫中,對於 SQL1 的寫法是實現了成功注入。
<a href="./inject.php?type=gbk&id=2341' or 1 = 1 -- asdf">SQL注入(針對用引號包括寫法)</a>
<a href="./inject.php?type=utf8&id=2341' or 1 = 1 -- asdf">SQL注入(針對用引號包括寫法)</a>
從測試的結果中,能夠知道,對於兩種編碼,都實現了對 SQL2 寫法的注入攻擊。
<a href="./inject.php?type=gbk&isslashes=1&id=2341<?php echo chr(0xbf) . chr(0x27); ?> or 1 = 1 -- asd">SQL注入(針對對特殊字符轉義寫法)</a>
<a href="./inject.php?type=utf8&isslashes=1&id=2341<?php echo chr(0xbf) . chr(0x27); ?> or 1 = 1 -- asd">SQL注入(針對對特殊字符轉義寫法)</a>
其中在連接中輸出 chr(0xbf) . chr(0x27) ,主要是爲了測試多字節編碼的值,通過轉義後,對於GBK存在BUG,addslashes處理後,前一個字符會被轉義爲一個多字節的合法字符,然後面的 chr(0x27) 則自動成了一個單引號,從而實現字符串截斷,形成注入。而相對於GBK,UTF8編碼的數據庫,在編碼轉義過程當中,不存在該問題。
GBK表現(注入成功)
UTF8表現
對於第三種寫法,由於 ID 在查詢過程當中,明確是整型數值,所以,在查詢前,強制進行整型校驗,因此在這幾種攻擊方式中,都沒能實現注入,所以,對於此種類型的數據查詢條件,爲了安全,咱們能夠進行相應的校驗,從而保證安全,但對於其餘類型的變量,則不太好處理。
綜合上面的幾種表現,最好的方式是以 PDO 方式來實現數據庫的查詢邏輯,只需注意如下幾點便可:
1. 參數 PDO::ATTR_EMULATE_PREPARES 設置爲 false;
2. 你不能讓佔位符 ? 代替一組值,如:SELECT * FROM blog WHERE userid IN ( ? );
3. 你不能讓佔位符代替數據表名或列名,如:SELECT * FROM blog ORDER BY ?;
4. 你不能讓佔位符 ? 代替任何其餘SQL語法,如:SELECT EXTRACT( ? FROM datetime_column) AS variable_datetime_element FROM blog;
4、總結
sql注入,能夠從多方面來進行防範,在可能的狀況下,若是數據庫須要存儲多字節字符,建議數據庫的編碼方式儘可能採用UTF8;另外,在查詢數據庫的邏輯實現過程當中,咱們要增強對於語句編寫方面的一些安全意識,如,對於整型數值進行強制性的類型校驗等;在解決方案上,建議採用 PDO 方式來實現相關的數據庫操做邏輯。
而對於另外幾種攻擊方式,區別在於傳送攻擊的方式不一樣,原理仍是同樣,因此再也不贅敘。
附件:
附件列表