字元約束條件的SQL注入

2022-05-30 13:27:11 字數 4512 閱讀 9493

引言

目前值得高興的是,開發者在建立**時,已經開始關注安全問題了——幾乎每個開發者都知道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)  

else  

} 為了驗證登入資訊,將用到下列**:

<?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)  

} 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 -p 

mysql> create database testing; 

query ok, 1 row affected (0.03 sec) 

mysql> use testing; 

database changed 

我將建立乙個資料表users,它有兩列,即username和password。並且,這兩個欄位的最大長度為25個字元。接下來,我將插入一行記錄,其中以「vampire」作為使用者名稱,以「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. 為了增加安全性,您還可以手動方式將輸入引數修剪為特定長度(具體長度可以視資料庫的中設定而定)。

基於約束條件的SQL攻擊

目前值得高興的是,開發者在建立 時,已經開始關注安全問題了 幾乎每個開發者都知道sql注入漏洞了。在本文中,我將為讀者介紹另一種與sql資料庫相關的漏洞,雖然它的危害性與sql注入不相上下,但目前卻很少為人所知。接下來,我將為讀者詳細展示這種攻擊手法,以及相應的防禦策略。背景知識 最近,我遇到了一段...

MySQL 約束條件

1 非空約束 not null規定某個欄位在插入的時候不能有null,標誌位非空的時候插入的時候必須給值,不然會報錯 2 唯一約束 unique規定某個字段在整個這一列中是唯一 3 主鍵 非空且唯一是主要特徵。主鍵可以唯一標識一行資料 可以從多行資料中定位到該資料 但是唯一標識一行資料的字段 或字段...

約束條件 CONSTRAINTS

檢視某個表的約束和索引 一 定義 對錶中的資料進行限定,保證資料的正確性 有效性 和完整性。二 分類 在建立表時,新增主鍵 id int primary key,給id 新增主鍵約束 建立完錶後,新增主鍵 自動增長 id int primary key auto increment,給主鍵id 新增...