1. 程式人生 > 其它 >sql注入程式碼分析及預防

sql注入程式碼分析及預防

sql注入的原因,表面上說是因為 拼接字串,構成sql語句,沒有使用 sql語句預編譯,繫結變數。但是更深層次的原因是,將使用者輸入的字串,當成了 “sql語句” 來執行。

1.union注入攻擊

<?php
$con=mysqli_connect("localhost","root","","test");
// 檢測連線
if (mysqli_connect_errno())
{
echo "連線失敗: " . mysqli_connect_error();
}

$id = $_GET['id'];

$result = mysqli_query($con,"select * from users where `id`=".$id);



while($row = mysqli_fetch_array($result))
{
echo $row['username'] . " " . $row['address'];
echo "<br>";
}
?>

當前端通過get方法傳參進來之後,如果沒有進行判斷,或者過濾一些非法的字串,這些非法的字串,就會和$result = mysqli_query($con,"select * from users where `id`=".$id);拼接在一起,然後傳入後臺資料庫,進行sql語句查詢,然後資料庫會返回來所查詢到的資訊,以至於洩露使用者的賬號及其密碼等敏感資訊,因此程式設計師在編寫程式時,一定要對傳入的引數值進行過濾。

2.boolean注入攻擊

<?php
$con=mysqli_connect("localhost","root","","test");
// 檢測連線
if (mysqli_connect_errno())
{
echo "連線失敗: " . mysqli_connect_error();
}

$id = $_GET['id'];

if (preg_match("/union|sleep|benchmark/i", $id)) {
exit("no");
}

$result = mysqli_query($con,"select * from users where `id`='".$id."'");

$row = mysqli_fetch_array($result);



if ($row) {
exit("yes");
}else{
exit("no");
}


?>

3.報錯注入

<?php
$con=mysqli_connect("localhost","root","","test");
// 檢測連線
if (mysqli_connect_errno())
{
echo "連線失敗: " . mysqli_connect_error();
}

$username = $_GET['username'];

if($result = mysqli_query($con,"select * from users where `username`='".$username."'")){
echo "ok";
}else{
echo mysqli_error($con); /*這個語句本不應該在這裡出現的,在編寫程式時,程式設計師為了檢查程式碼,然後造成了報錯sql注入,如果將輸出語句改成 echo "no",即便查詢語句傳入資料庫,但也沒辦法在前端              顯示,但是還是要對傳入的引數進行過濾的*/
}

?>

通過上面兩個例項,我們對sql注入有了初步的瞭解,平時應該怎樣防禦sql注入呢?

4.sql注入的防禦

1>採用sql語句預編譯和繫結變數,是防禦sql注入的最佳方法。

String sql = "select * from test where id=?";

PreparedStatement ps = conn.prepareStatement(sql);

ps.setInt(1,id);

ps.executeQuery();

通過採用PreparedStatement,就會將sql語法 "select * from test where id=?" 預先編譯好,也就是SQL引擎會預先進行語法分析,產生語法樹,生成執行計劃,也就是說,後面你輸入的引數,無論你輸入的是什麼,都不會影響sql語句的語法結構,因為語法分析已經完成了,而語法分析主要是分析sql命令,如select,from,等等,所以即便你後面輸入了這些sql命令,也不會當成sql命令來執行,因為這些sql命令的執行,必須先得通過語法分析,生成執行計劃,既然語法分析已經完成,已經預編譯過了,那麼後面輸入的引數,是絕對不可能作為sql命令來執行的,只會被當做字串字面值引數。所以sql語句預編譯可以防禦sql注入

2>除了以上得方法,我們還需要使用一些安全函式來防止sql注入,因為不是所有場景都能夠採用 sql語句預編譯,有一些場景必須的採用 字串拼接的方式。

比如 String sql = "select * from test where id =" +id;

在接收到使用者輸入的引數時,我們就嚴格檢查 id,只能是int型。複雜情況可以使用正則表示式來判斷。這樣也是可以防止sql注入的。

MySQLCodec codec = new MySQLCodec(Mode.STANDARD);
name = ESAPI.encoder().encodeForSQL(codec, name);
String sql = "select * from test where name=" + name;
ESAPI.encoder().encodeForSQL(codec, name)
該函式會將 name 中包含的一些特殊字元進行編碼,這樣 sql 引擎就不會將name中的字串當成sql命令來進行語法分析了。