PHP SQL 插入式攻擊預防辦法與注入分析

來源:互聯網
上載者:User

1. php教程 設定檔 php.ini 中的 magic_quotes_gpc 選項沒有開啟,被置為 off 2. 開發人員沒有對資料類型進行檢查和轉義

不過事實上,第二點最為重要。我認為, 對使用者輸入的資料類型進行檢查,向 mysql教程 提交正確的資料類型,這應該是一個 web 程式員最最基本的素質。但現實中,常常有許多小白式的 web 開發人員忘了這點, 從而導致後門大開。

為什麼說第二點最為重要?因為如果沒有第二點的保證,magic_quotes_gpc 選項,不論為 on,還是為 off,都有可能引發 sql 注入攻擊。下面來看一下技術實現:

一. magic_quotes_gpc = off 時的注入攻擊

magic_quotes_gpc = off 是 php 中一種非常不安全的選項。新版本的 php 已經將預設的值改為了 on。但仍有相當多的伺服器的選項為 off。畢竟,再古董的伺服器也是有人用的。

當magic_quotes_gpc = on 時,它會將提交的變數中所有的 '(單引號)、"(雙號號)、(反斜線)、空白字元,都為在前面自動加上 。下面是 php 的官方說明:

view sourceprint?

 

magic_quotes_gpc boolean

 

sets the magic_quotes state for gpc (get/post/cookie) operations. when magic_quotes are on, all ' (single-quote), " (double quote), (backslash) and nul's are escaped with a backslash automatically


如果沒有轉義,即 off 情況下,就會讓攻擊者有機可乘。以下列測試指令碼為例:

 1<?
 2if ( isset($_post["f_login"] ) )
 3{
 4  // 串連資料庫教程...
 5  // ...代碼略...
 6
 7  // 檢查使用者是否存在
 8  $t_struname = $_post["f_uname"];
 9  $t_strpwd = $_post["f_pwd"];
10  $t_strsql = "select * from tbl_users where username='$t_struname' and password = '$t_strpwd' limit 0,1";
11
12  if ( $t_hres = mysql_query($t_strsql) )
13  {
14    // 成功查詢之後的處理. 略...
15  }
16}
17?>
18
19<html><head><title>sample test</title></head>
20<body>
21<form method=post action="">
22  username: <input type="text" name="f_uname" size=30><br>
23  password: <input type=text name="f_pwd" size=30><br>
24
25  <input type="submit" name="f_login" value="登入">
26</form>
27</body>

 

在這個指令碼中,當使用者輸入正常的使用者名稱和密碼,假設值分別為 zhang3、abc123,則提交的 sql 語句如下:

1 select * from tbl_users
2 where username='zhang3' and password = 'abc123' limit 0,1

 


如果攻擊者在 username 欄位中輸入:zhang3' or 1=1 #,在 password 輸入 abc123,則提交的 sql 語句變成如下:

1 select * from tbl_users
2 where username='zhang3' or 1=1 #' and password = 'abc123' limit 0,1

 

 

 


由於 # 是 mysql中的注釋符, #之後的語句不被執行,實現上這行語句就成了:

1 select * from tbl_users
2 where username='zhang3' or 1=1

 

 

 


這樣攻擊者就可以繞過認證了。如果攻擊者知道資料庫結構,那麼它構建一個 union select,那就更危險了:

假設在 username 中輸入:zhang3 ' or 1 =1 union select cola, colb,cold from tbl_b #

在password 輸入: abc123,

則提交的 sql 語句變成:

1 select * from tbl_users
2 where username='zhang3 '
3 or 1 =1 union select cola, colb,cold from tbl_b #' and password = 'abc123' limit 0,1

 

 

 


這樣就相當危險了。如果agic_quotes_gpc選項為 on,引號被轉義,則上面攻擊者構建的攻擊語句就會變成這樣,從而無法達到其目的:

1 select * from tbl_users
2 where username='zhang3' or 1=1 #'
3 and password = 'abc123'
4 limit 0,1
5
6 select * from tbl_users
7 where username='zhang3 ' or 1 =1 union select cola, colb,cold from tbl_b #'
8 and password = 'abc123' limit 0,1

 

 

 


二. magic_quotes_gpc = on 時的注入攻擊

當 magic_quotes_gpc = on 時,攻擊者無法對字元型的欄位進行 sql 注入。這並不代表這就安全了。這時,可以通過數值型的欄位進行sql注入。

在最新版的 mysql 5.x 中,已經嚴格了資料類型的輸入,已預設關閉自動類型轉換。數值型的欄位,不能是引號標記的字元型。也就是說,假設 uid 是數值型的,在以前的 mysql 版本中,這樣的語句是合法的:

1 insert into tbl_user set uid="1";
2 select * from tbl_user where uid="1";

 

 

 


在最新的 mysql 5.x 中,上面的語句不是合法的,必須寫成這樣:

1 insert into tbl_user set uid=1;
2 select * from tbl_user where uid=1;

 

 

 


這樣我認為是正確的。因為作為開發人員,向資料庫提交正確的符合規則的資料類型,這是最基本的要求。

那麼攻擊者在 magic_quotes_gpc = on 時,他們怎麼攻擊呢?很簡單,就是對數值型的欄位進行 sql 注入。以下列的 php 指令碼為例:

 1 <?
 2  if ( isset($_post["f_login"] ) )
 3 {
 4   // 串連資料庫...
 5   // ...代碼略...
 6 
 7   // 檢查使用者是否存在
 8    $t_struid = $_post["f_uid"];
 9   $t_strpwd = $_post["f_pwd"];
10   $t_strsql = "select * from tbl_users where uid=$t_struid and password = '$t_strpwd' limit 0,1";
11   if ( $t_hres = mysql_query($t_strsql) )
12   {
13     // 成功查詢之後的處理. 略...
14   }
15
16 }
17 ?>
18 <html><head><title>sample test</title></head>
19 <body>
20 <form method=post action="">
21   user id: <input type="text" name="f_uid" size=30><br>
22
23   password: <input type=text name="f_pwd" size=30><br>
24   <input type="submit" name="f_login" value="登入">
25 </form>
26 </body>

 

 

 


上面這段指令碼要求使用者輸入 userid 和 password 登入。一個正常的語句,使用者輸入 1001和abc123,提交的 sql 語句如下:

select * from tbl_users where userid=1001 and password = 'abc123' limit 0,1

 

 

 

 

如果攻擊者在 userid 處,輸入:1001 or 1 =1 #,則注入的sql語句如下:

select * from tbl_users where userid=1001 or 1 =1 # and password = 'abc123' limit 0,1

 

 

 

 

攻擊者達到了目的。

三. 如何防止 php sql 注入攻擊

如何防止 php sql 注入攻擊?我認為最重要的一點,就是要對資料類型進行檢查和轉義。總結的幾點規則如下:

php.ini 中的 display_errors 選項,應該設為 display_errors = off。這樣 php 指令碼出錯之後,不會在 web 頁面輸出錯誤,以免讓攻擊者分析出有作的資訊。

調用 mysql_query 等 mysql 函數時,前面應該加上 @,即 @mysql_query(...),這樣 mysql 錯誤不會被輸出。同理以免讓攻擊者分析出有用的資訊。另外,有些程式員在做開發時,當 mysql_query出錯時,習慣輸出錯誤以及 sql 語句,例如:  1 $t_strsql = "select a from b....";
 2 if ( mysql_query($t_strsql) )
 3 {
 4   // 正確的處理
 5 }
 6 else
 7 {
 8   echo "錯誤! sql 語句:$t_strsql rn錯誤資訊".mysql_query();
 9   exit;
10 }

 

 

 
 

這種做法是相當危險和愚蠢的。如果一定要這麼做,最好在網站的設定檔中,設一個全域變數或定義一個宏,設一下 debug 標誌:

 1 //全域設定檔中:
 2 define("debug_mode",0);    // 1: debug mode; 0: release mode
 3
 4 //呼叫指令碼中:
 5 $t_strsql = "select a from b....";
 6 if ( mysql_query($t_strsql) )
 7 {
 8   // 正確的處理
 9 }
10 else
11 {
12   if (debug_mode)
13     echo "錯誤! sql 語句:$t_strsql rn錯誤資訊".mysql_query();
14   exit;
15 }

 

 

 
 

 

對提交的 sql 語句,進行轉義和類型檢查。

 

四. 我寫的一個安全參數擷取函數

為了防止使用者的錯誤資料和 php + mysql 注入 ,我寫了一個函數 papi_getsafeparam(),用來擷取安全的參數值:

 1 define("xh_param_int",0);
 2 define("xh_param_txt",1);
 3 function papi_getsafeparam($pi_strname, $pi_def = "", $pi_itype = xh_param_txt)
 4 {
 5   if ( isset($_get[$pi_strname]) )
 6     $t_val = trim($_get[$pi_strname]);
 7   else if ( isset($_post[$pi_strname]))
 8     $t_val = trim($_post[$pi_strname]);
 9   else
10     return $pi_def;
11
12   // int
13   if ( xh_param_int == $pi_itype)
14   {
15     if (is_numeric($t_val))
16       return $t_val;
17     else
18       return $pi_def;
19   }
20  
21   // string
22   $t_val = str_replace("&", "&amp;",$t_val);
23   $t_val = str_replace("<", "&lt;",$t_val);
24   $t_val = str_replace(">", "&gt;",$t_val);
25   if ( get_magic_quotes_gpc() )
26   {
27     $t_val = str_replace(""", "&quot;",$t_val);
28     $t_val = str_replace("''", "&#039;",$t_val);
29   }
30   else
31   {
32     $t_val = str_replace(""", "&quot;",$t_val);
33     $t_val = str_replace("'", "&#039;",$t_val);
34   }
35   return $t_val;
36 }

 

 

 


在這個函數中,有三個參數:

$pi_strname: 變數名
$pi_def: 預設值
$pi_itype: 資料類型。取值為 xh_param_int, xh_param_txt, 分別表示數值型和文本型。

 

 

 


如果請求是數值型,那麼調用 is_numeric() 判斷是否為數值。如果不是,則返回程式指定的預設值。

簡單起見,對於文本串,我將使用者輸入的所有危險字元(包括html代碼),全部轉義。由於 php 函數 addslashes()存在漏洞,我用 str_replace()直接替換。get_magic_quotes_gpc() 函數是 php 的函數,用來判斷 magic_quotes_gpc 選項是否開啟。

剛才第二節的樣本,代碼可以這樣調用:

 1 <?
 2 if ( isset($_post["f_login"] ) )
 3 {
 4   // 串連資料庫...
 5   // ...代碼略...
 6 
 7   // 檢查使用者是否存在
 8   $t_struid = papi_getsafeparam("f_uid", 0, xh_param_int);
 9   $t_strpwd = papi_getsafeparam("f_pwd", "", xh_param_txt);
10   $t_strsql = "select * from tbl_users where uid=$t_struid and password = '$t_strpwd' limit 0,1";
11   if ( $t_hres = mysql_query($t_strsql) )
12   {
13     // 成功查詢之後的處理. 略...
14   }
15 }
16 ?>

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.