1. 程式人生 > >SQL注入基礎

SQL注入基礎

背景

最近部門搞一次安全大賽,屬於業餘級別的,面向沒有系統黑客知識的參賽者。我本以為沒有多少人會參加的,沒有想到大家紛紛抱團組隊,誓要拿個冠軍回來。

當然我也投入到比賽的大軍之中,在比賽的這一週工作上的事情很多,根本抽不出時間查詢相關的解題技巧,當然其它同事也是類似的情況。所以我重點關注了兩道注入相關的題目,其中之一是SQL注入。

在整個解題過程中,網上只找到一篇詳細講解SQL注入的文章,是轉載的,但原文已經找不到了。

對於我來說,自工作以來就沒有用過資料庫,各種SQL語法還停留在讀書時代的階段,甚至連程式設計程式碼中是如何拼接成完整的SQL語句都想像不出來。所以本文是以一個實驗者的角度來動手做一次SQL注入的實驗。

既然是實驗性的,那得搭建環境了。作為新手,為了省卻複雜性帶來不必要的麻煩,我選取了最簡單的配置,即Ubuntu + Apache + PHP + MySQL。只需要一個簡單的安裝、建立資料庫和表、以及寫一個簡單的php網頁即可。

何謂為SQL注入

在正式開始之前,我們先講講什麼是SQL注入。

還記得小學語文考試上的填空題嗎?

我是_______________,喜歡___________________

題目的意圖明顯是通過填空來了解答題者的名字和愛好。

如果填成下面這樣呢?

我是調皮的小男孩子,專職撩妹子。,喜歡_____________

這就是一個注入的例子,當出題者以為他已經定下了句子的主體結構,需要填空的內容是不會影響主體結構的,而填空者卻通過填寫的內容,修改了整句話的結構,這就是注入。

以王寶強馬蓉兩人為例,對於王寶強來說,請來的經紀人是工作上的一部分,這也是他定下來的人生結構。他卻沒有想到,經紀人除了完成經紀人所應做的事情,還在背後幹起別的事情來。對於王寶強來說,經紀人做的事情,就是一種注入,改變了他定義的結構組織。

那什麼是SQL注入就不言而喻了。當黑客通過精心構造的URL引數,或者表單提交的引數,拼接到預先定義好的SQL格式時,意外地改變了程式設計師預期的SQL結構時,SQL注入就構成了。執行該SQL語句已超出的程式設計師的意圖。比如,沒用使用者名稱或密碼可以登陸成功,或將資料庫內容全部dump下來,等等。

安裝Apach, PHP和MySQL

為什麼選擇LAMP(Linux + Apache + MySQL + PHP)呢? 因為我對Windows下的開發環境不熟悉,而在Linux下較輕量級的Web環境搭建要數LAMP了。

如果你有Web動態語言開發經驗,php是個很容易上手的語言。當然我們選用LAMP的目的不是Web本身,是使用它來寫一個demo的資料庫應用,然後重點測試SQL注入,所以重點是SQL注入,LAMP只是配菜。

在Ubuntu下安全LAMP非常簡單,只需要一個命令就可以了:

sudo apt-get install apache2 mysql-server mysql-client php5 php5-gd php5-mysql

在安裝MySQL過程中會要求設定root使用者的密碼,設定成root(後面連結資料庫時需要)。

安裝完成後,web專案根目錄預設在/var/www/,為了簡單起來,對LAMP不做任何配置。開啟瀏覽器,輸入測試安裝是否成功。下是測試成功的例子。

appche測試效果

然後測試一下php5是否工作正常:

  1. sudo touch /var/www/test.php
  2. sudo vim /var/www/test.php ,並輸入以下內容
<?php
    phpinfo()
?>
  1. 在瀏覽器開啟 http://localhost/test.php,輸出結果如下:
    php5測試效果

好了,實驗環境搭建成功了。

編寫簡單的資料庫應用

要一步步學習SQL注入,少不了簡單的資料庫應用,在這裡開發一個簡單的demo作為測試物件。幾乎所有的應用學習都以使用者登陸開始,在這裡我們也不免俗。

建立資料庫

連結資料庫

使用mysql客戶端連線MySQL資料,命令如下:

#mysql -h localhost -u root -p
Enter password: <這裡輸入密碼為root>

輸入使用者root的密碼root成功登入之後的過程:
連結mysql資料庫

建立資料庫test

首先要建立一個數據庫,這裡將資料名字稱為test,在mysql客戶端輸入如下命令:

mysql>create database test;

然後進入test資料庫

mysql> use test;

建立表userinfo

有了資料庫,還得有表,才能裝下真正的資料。簡單起見,表只有兩個欄位name和passwd,分別儲存使用者名稱和密碼,其中name為主鍵。MySQL下的建立語句:

create table userinfo (name char(20) not null primary key, passwd char(20) not null);

插入使用者資料

為了測試,需要往表裡面插入資料。使用mysql語句往userinfo表插入兩個使用者資訊,分別是linyt和ivan,密碼和使用者名稱內空一樣。

MySQL語句如下:

insert into userinfo values('linyt', 'linyt');
insert into userinfo values('ivan','ivan');

測試一下資料表內容

在MySQL客戶端輸入:select * from userinfo;命令可以看到userinfo表中所有記錄。

userinfo表中所有記錄

編寫php應用demo

下面使用php指令碼語言編寫個簡單的登入系統,僅僅為演示目的。程式碼如下:
php程式碼片段1
php程式碼片段2

本應用原理非常簡單,通過username和passwd兩個文字框接受使用者輸入的使用者名稱和密碼,然連結資料庫,使用select * from userinfo where name='<輸入的使用者名稱>' AND passwd='<使用者輸入的密碼>'SQL語句從資料庫中查詢出使用者記錄,如果有該用名,則以表格方式來顯示使用者資訊。

對於沒人做過web開發的朋友可以會問,這裡為什麼需要單引號',那是因為name欄位的型別是字串,字串值需要使用單引號'括起來。而數值型別是不需的單引號'的。

為了方便後面驗證展示,每次都會在網頁上輸出查詢的SQL語句內容。

SQL注入實驗

前面都是餐前菜,這裡才是主菜,各位久等了。在注入實驗之前,我們先展示正常登入效果:

正常登入效果

先來觀察一下發生了什麼事情:

  1. 分別輸入使用者名稱linyt和密碼linyt,然後點login
  2. 後面php服務中獲取使用者的輸入,並儲存在$name$passwd兩個變數
  3. 使用$name$passwd兩變數來拼接生成SQL語句,並進行資料庫查詢
  4. 將從資料庫是查詢出資料在網頁上輸出

本例中輸入使用者名稱linyt和密碼linyt情況,SQL語句的拼接過程如下:
正常情況下,sql語句

如果不知道使用者名稱和密碼的情況下,怎麼能夠登陸成功呢?這是SQL注入攻擊的初衷。
如果不知道使用者名稱密碼,那麼name = ‘$name’ 隨便替換成一個使用者名稱如foo,變成name = ‘foo’,這個條件肯定不能為true,為了使得WHERE語句的條件為ture,就得更改它的語句結構。

即然name = ‘foo’ 肯定為false,那麼我們在後面增加一個or,再附件一個恆真語句,那整個語句就為ture了。恆真語句非常多,比如'1' = '1'1=1, '' = '',在不同的上下文選擇適合的就可以了。

留意一下SQL拼接語句中name = ‘$name’,這裡是有兩個'號的。為了最終注入結果為:

select * from userinfo WHERE name = '<這裡寫什麼都不重要>' or '1'='1' AND passwd = '<這裡寫什麼都不重要>' or '1'='1'

那name和passwd應該填什麼呢?請看圖:

注入構造過程

至於,為什麼第一個字元中 ',而且最後一個1後面沒有',請大家補腦對照一下,這裡不細說。

下面是注入測試結果:
注入測試結果

請注意瀏覽器位址列的內容:http://localhost/uerinfo.php?name='+or+'1'%3D'1&passwd='+or+'1'%3D'1

瀏覽器在址址顯示時使用了URL編碼,與使用者輸入username和passwd內容看起來不完全一樣,實際是一樣的。使用+來代表空格,%3D來代替=

通過注入,網頁顯示了userinfo表中的所有記錄。 大家可以再看一下網頁上的SQL語句輸出:sql = SELECT * FROM userinfo WHERE name = '' or '1'='1' AND passwd = '' or '1'='1'

如果你對SQL很在行,那可做更復雜的注入,可以將資料庫裡面很多東西給偷出來。

結束

SQL注入的故事到這裡,可以告一段落了。擁有這些技能是否可獨立行走江湖了呢?答案是不行。這裡的SQL注入技能,只是從0到1的突破,從開發者腦洞大開,向攻擊思維轉身。但如果說SQL注入的江湖就這些小,那就顯示過於自大了,會死得很慘的。

有攻就有防,即然大家都知道直接使用輸入來拼接生成SQL很容易受到攻擊,那麼開發人員肯定會使用各種招數防止被注入。典型的招數比如使用正式表示式過濾非法輸入,對特殊字元做轉義再傳給MySQL資料庫,或者使用安全的引數法煮熟法

來吧,如果你沒有實踐過SQL注入,本文就給你來個0到1的轉變。