對於sql注入與防注入其實就是一個攻與防的,今天我們要告訴大家最基本的注入和防止方法,原理都是利用了php或mysql的一些特性而我們沒注意所造成的。
一個簡單的SQL注入攻擊案例
假如我們有一個公司網站,在網站的後台數據庫中保存了所有的客戶數據等重要信息。假如網站登錄頁面的代碼中有這樣一條命令來讀取用戶信息。
<?
$q = "SELECT `id` FROM `users` WHERE `username`= ' " .$_GET['username']. " ' AND `password`= ' " .$_GET['password']. " ' ";
?>
現在有一個黑客想攻擊你的數據庫,他會嘗試在此登錄頁面的用戶名的輸入框中輸入以下代碼:
代碼如下 復制代碼' ; SHOW TABLES;
點擊登陸鍵,這個頁面就會顯示出數據庫中的所有表。如果他現在使用下面這行命令:
代碼如下 復制代碼'; DROP TABLE [table name];
這樣他就把一張表刪除了!
當然,這只是一個很簡單的例子,實際的SQL注入方法比這個要復雜得多,黑客也願意花大量的時間來不斷嘗試來攻擊你的代碼。有一些程序軟件也可以自動地來不斷嘗試SQL注入攻擊。了解了SQL注入的攻擊原理後,我們來看一下如何防范SQL注入攻擊。
magic_quotes_gpc = On 時的注入攻擊
當 magic_quotes_gpc = On 時,攻擊者無法對字符型的字段進行 SQL 注入。這並不代表這就安全了。這時,可以通過數值型的字段進行SQL注入。
在最新版的 MYSQL 5.x 中,已經嚴格了數據類型的輸入,已默認關閉自動類型轉換。數值型的字段,不能是引號標記的字符型。也就是說,假設 uid 是數值型的,在以前的 mysql 版本中,這樣的語句是合法的:
代碼如下 復制代碼INSERT INTO tbl_user SET uid="1";
SELECT * FROM tbl_user WHERE uid="1";
在最新的 MYSQL 5.x 中,上面的語句不是合法的,必須寫成這樣:
代碼如下 復制代碼INSERT INTO tbl_user SET uid=1;
SELECT * FROM tbl_user WHERE uid=1;
這樣我認為是正確的。因為作為開發者,向數據庫提交正確的符合規則的數據類型,這是最基本的要求。
那麼攻擊者在 magic_quotes_gpc = On 時,他們怎麼攻擊呢?很簡單,就是對數值型的字段進行 SQL 注入。以下列的 php 腳本為例:
代碼如下 復制代碼<?
if ( isset($_POST["f_login"] ) )
{
// 連接數據庫...
// ...代碼略...
// 檢查用戶是否存在
$t_strUid = $_POST["f_uid"];
$t_strPwd = $_POST["f_pwd"];
$t_strSQL = "SELECT * FROM tbl_users WHERE uid=$t_strUid AND password = '$t_strPwd' LIMIT 0,1";
if ( $t_hRes = mysql_query($t_strSQL) )
{
// 成功查詢之後的處理. 略...
}
}
?>
<html><head><title>sample test</title></head>
<body>
<form method=post action="">
User ID: <input type="text" name="f_uid" size=30><br>
Password: <input type=text name="f_pwd" size=30><br>
<input type="submit" name="f_login" value="登錄">
</form>
</body>
上面這段腳本要求用戶輸入 userid 和 password 登入。一個正常的語句,用戶輸入 1001和abc123,提交的 sql 語句如下:
如果攻擊者在 userid 處,輸入:1001 OR 1 =1 #,則注入的sql語句如下:
代碼如下 復制代碼 SELECT * FROM tbl_users WHERE userid=1001 OR 1 =1 # AND password = 'abc123' LIMIT 0,1攻擊者達到了目的。
防范SQL注入 - 使用mysql_real_escape_string()函數
在數據庫操作的代碼中用這個函數mysql_real_escape_string()可以將代碼中特殊字符過濾掉,如引號等。如下例:
代碼如下 復制代碼<?
$q = "SELECT `id` FROM `users` WHERE `username`= ' " .mysql_real_escape_string( $_GET['username'] ). " ' AND `password`= ' " .mysql_real_escape_string( $_GET['password'] ). " ' ";
?>
防范SQL注入 - 使用mysql_query()函數
mysql_query()的特別是它將只執行SQL代碼的第一條,而後面的並不會執行。回想在最前面的例子中,黑客通過代碼來例後台執行了多條SQL命令,顯示出了所有表的名稱。所以mysql_query()函數可以取到進一步保護的作用。我們進一步演化剛才的代碼就得到了下面的代碼:
代碼如下 復制代碼<?
//connection
$database = mysql_connect("localhost", "username","password");
//db selection
mysql_select_db("database", $database);
$q = mysql_query("SELECT `id` FROM `users` WHERE `username`= ' " .mysql_real_escape_string( $_GET['username'] ). " ' AND `password`= ' " .mysql_real_escape_string( $_GET['password'] ). " ' ", $database);
?>
除此之外,我們還可以在PHP代碼中判斷輸入值的長度,或者專門用一個函數來檢查輸入的值。所以在接受用戶輸入值的地方一定要做好輸入內容的過濾和檢查。當然學習和了解最新的SQL注入方式也非常重要,這樣才能做到有目的的防范。如果使用的是平台式的網站系統如Wordpress,要注意及時打上官方的補丁或升級到新的版本。如果有講得不對的地方或不理解的請在評論區留言。
php.ini 中的 display_errors 選項,應該設為 display_errors = off。這樣 php 腳本出錯之後,不會在 web 頁面輸出錯誤,以免讓攻擊者分析出有作的信息。
調用 mysql_query 等 mysql 函數時,前面應該加上 @,即 @mysql_query(...),這樣 mysql 錯誤不會被輸出。同理以免讓攻擊者分析出有用的信息。另外,有些程序員在做開發時,當 mysql_query出錯時,習慣輸出錯誤以及 sql 語句,例如:
這種做法是相當危險和愚蠢的。如果一定要這麼做,最好在網站的配置文件中,設一個全局變量或定義一個宏,設一下 debug 標志:
全局配置文件中:
代碼如下 復制代碼 define("DEBUG_MODE",0); // 1: DEBUG MODE; 0: RELEASE MODE//調用腳本中:
代碼如下 復制代碼 $t_strSQL = "SELECT a from b....";
關於 sql防注入內容 http://www.bKjia.c0m/phper/phpanqn/37704.htm