php防止sql注入的方法
一.什么是SQL注入式攻擊?
????????所謂SQL注入式攻擊,就是攻擊者把SQL命令插入到Web表單的輸入域或頁面請求的查詢字符串,欺騙服務(wù)器執(zhí)行惡意的SQL命令。在某些表單中,用戶輸入的內(nèi)容直接用來構(gòu)造(或者影響)動態(tài)SQL命令,或作為存儲過程的輸入?yún)?shù),這類表單特別容易受到SQL注入式攻擊。
例如一個簡單的登錄表單(這里把密碼寫成明文方便說明):?
?
當(dāng)在表單中填寫這樣的語句進行提交登錄時會出現(xiàn)這樣的SQL語句
這樣會查詢出所有的用戶信息,所有存在不安全隱患
二.常見的SQL注入式攻擊過程類如:
⑴ 某個php Web應(yīng)用有一個登錄頁面,這個登錄頁面控制著用戶是否有權(quán)訪問應(yīng)用,它要求用戶輸入一個名稱和密碼
⑵ 登錄頁面中輸入的內(nèi)容將直接用來構(gòu)造動態(tài)的SQL命令,或者直接用作存儲過程的參數(shù)
⑶ 攻擊者在用戶名字和密碼輸入框中輸入"'或'1'='1"之類的內(nèi)容
⑷ 用戶輸入的內(nèi)容提交給服務(wù)器之后,服務(wù)器運行上面的php代碼構(gòu)造出查詢用戶的SQL命令,但由于攻擊者輸入的內(nèi)容非常特殊,所以最后得到的SQL命令變成:SELECT * from Users WHERE login = '' or '1'='1' AND password = '' or '1'='1'.
⑸ 服務(wù)器執(zhí)行查詢或存儲過程,將用戶輸入的身份信息和服務(wù)器中保存的身份信息進行對比
⑹ 由于SQL命令實際上已被注入式攻擊修改,已經(jīng)不能真正驗證用戶身份,所以系統(tǒng)會錯誤地授權(quán)給攻擊者
如果攻擊者知道應(yīng)用會將表單中輸入的內(nèi)容直接用于驗證身份的查詢,他就會嘗試輸入某些特殊的SQL字符串篡改查詢改變其原來的功能,欺騙系統(tǒng)授予訪問權(quán)限。
系統(tǒng)環(huán)境不同,攻擊者可能造成的損害也不同,這主要由應(yīng)用訪問數(shù)據(jù)庫的安全權(quán)限決定。如果用戶的帳戶具有管理員或其他比較高級的權(quán)限,攻擊者就可能對數(shù)據(jù)庫的表執(zhí)行各種他想要做的操作,包括添加、刪除或更新數(shù)據(jù),甚至可能直接刪除表。
三.常用的SQL注入方式
1. 使用單引號及or關(guān)鍵字
SELECT * FROM Users WHERE Username='$username' AND Password='$password'?我們針對上面的SQL語句分析,發(fā)現(xiàn)如果用下面的測試數(shù)據(jù)就能夠進行SQL注入了?
?username=1′or′1′=′1password=1’or’1’=’1?
?看看整個SQL查詢語句變成:?
?假設(shè)參數(shù)值是通過GET方法傳遞到服務(wù)器的,且域名為www.example.com 那么我們的訪問請求就是:
http://www.example.com/index.php?username=1‘%20or%20’1’%20=%20’1password=1’%20or%20’1’%20=%20’1?
? 對上面的SQL語句作簡單分析后我們就知道由于該語句永遠為真,所以肯定會返回一些數(shù)據(jù),在這種情況下實際上并未驗證用戶名和密碼,并且在某些系統(tǒng)中,用戶表的第一行記錄是管理員,那這樣造成的后果則更為嚴重。
2. 使用括號
SELECT * FROM Users WHERE((Username='$username')AND(Password=MD5('$password')))?在這個例子中,存在兩個問題,一個是括號的用法,還有一個是MD5哈希函數(shù)的用法。對于第一個問題,我們很容找出缺少的右括號解決,對于第二個問題,我們可以想辦法使第二個條件失效。我們在查詢語句的最后加上一個注釋符以表示后面的都是注釋,常見的注釋起始符是/*(在Oracle中是–),也就是說,我們用如下的用戶名和密碼:?
?username=1′or′1′=′1′))/?password = foo??那么整條SQL語句就變?yōu)?#xff1a;?
??那么看看URL請求就變?yōu)?#xff1a;?
?http://www.example.com/index.php?username=1‘%20or%20’1’%20=%20’1’))/*&password=foo?
?#####
3.Union查詢SQL注入測試 ??
??Union查詢SQL注入測試 ? 還有一種測試是利用Union的,利用Union可以連接查詢,從而從其他表中得到信息,假設(shè)如下查詢:?
?然后我們設(shè)置id的值為:?
?$id =1 UNION ALL SELECT creditCardNumber,1,1 FROM CreditCarTable ,?那么整體的查詢就變?yōu)?#xff1a;?
?顯示這就能得到所有信用卡用戶的信息。 ??盲目SQL注入測試 ? 在上面我們提到過盲SQL注入,即bind SQL Injection,它意味著對于某個操作我們得不到任何信息,通常這是由于程序員已經(jīng)編寫了特定的出錯返回頁面,從而隱藏了數(shù)據(jù)庫結(jié)構(gòu)的信息。 ??
但利用推理方法,有時候我們能夠恢復(fù)特定字段的值。這種方法通常采用一組對服務(wù)器的布爾查詢,依據(jù)返回的結(jié)果來推斷結(jié)果的含義。仍然延續(xù)上面的www.example.com有一個參數(shù)名為id, 那么我們輸入以下url請求:?
??http://www.exampe.com/index.php?id=1’?
?顯然由于語法錯誤,我們會得到一個預(yù)先定義好的出錯頁面,假設(shè)服務(wù)器上的查詢語句為 :
?SELECT field1,field2,field3 FROM Users WHERE Id=’Id′假設(shè)我們想要的用戶名字段的值,那么通過一些函數(shù),我們就可以逐字符的讀取用戶名的值。在這里我們使用以下的函數(shù):SUBSTRING(text,start,length),ASCII(char),LENGTH(text)?我們定義id為:引用Id=1’ AND ASCII(SUBSTRING(username,1,1))=97 AND ‘1’=’1?
?那么最終的SQL查詢語句為:?
?那么,如果在數(shù)據(jù)庫中有用戶名的第一字符的ASCII碼為97的話,那么我們就能得到一個真值u,那么就繼續(xù)尋找該用戶名的下一個字符;如果沒有的話,那么我們就增猜測第一個字符的ASCII碼為98的用戶名,這樣反復(fù)下去就能判斷出合法的用戶名
四.如何防范?
只要在利用表單輸入的內(nèi)容構(gòu)造SQL命令之前,把所有輸入內(nèi)容過濾一番就可以了。過濾輸入內(nèi)容可以按多種方式進行:
?(1)對于動態(tài)構(gòu)造SQL查詢的場合,可以使用下面的技術(shù):
? 第一:替換單引號,即把所有單獨出現(xiàn)的單引號改成兩個單引號,防止攻擊者修改SQL命令的含義。再來看前面的例子,“SELECT * from Users WHERE login = ''' or ''1''=''1' AND password = ''' or ''1''=''1'”顯然會得到與“SELECT * from Users WHERE login = '' or '1'='1' AND password = '' or '1'='1'”不同的結(jié)果
??第二:刪除用戶輸入內(nèi)容中的所有連字符,防止攻擊者構(gòu)造出類如“SELECT * from Users WHERE login = 'mas' AND password =''”之類的查詢,因為這類查詢的后半部分已經(jīng)被注釋掉,不再有效,攻擊者只要知道一個合法的用戶登錄名稱,根本不需要知道用戶的密碼就可以順利獲得訪問權(quán)限
第三:對于用來執(zhí)行查詢的數(shù)據(jù)庫帳戶,限制其權(quán)限。用不同的用戶帳戶執(zhí)行查詢、插入、更新、刪除操作。由于隔離了不同帳戶可執(zhí)行的操作,因而也就防止了原本用于執(zhí)行SELECT命令的地方卻被用于執(zhí)行INSERT、UPDATE或DELETE命令
?(2)用存儲過程來執(zhí)行所有的查詢。SQL參數(shù)的傳遞方式將防止攻擊者利用單引號和連字符實施攻擊。此外,它還使得數(shù)據(jù)庫權(quán)限可以限制到只允許特定的存儲過程執(zhí)行,所有的用戶輸入必須遵從被調(diào)用的存儲過程的安全上下文,這樣就很難再發(fā)生注入式攻擊了。
?(3)限制表單或查詢字符串輸入的長度。如果用戶的登錄名字最多只有10個字符,那么不要認可表單中輸入的10個以上的字符,這將大大增加攻擊者在SQL命令中插入有害代碼的難度
?(4)檢查用戶輸入的合法性,確信輸入的內(nèi)容只包含合法的數(shù)據(jù)。數(shù)據(jù)檢查應(yīng)當(dāng)在客戶端和服務(wù)器端都執(zhí)行,之所以要執(zhí)行服務(wù)器端驗證,是為了彌補客戶端驗證機制脆弱的安全性??
?在客戶端,攻擊者完全有可能獲得網(wǎng)頁的源代碼,修改驗證合法性的腳本(或者直接刪除腳本),然后將非法內(nèi)容通過修改后的表單提交給服務(wù)器。因此,要保證驗證操作確實已經(jīng)執(zhí)行,唯一的辦法就是在服務(wù)器端也執(zhí)行驗證。你可以使用許多內(nèi)建的驗證對象,例如RegularExpressionValidator,它們能夠自動生成驗證用的客戶端腳本,當(dāng)然你也可以插入服務(wù)器端的方法調(diào)用。如果找不到現(xiàn)成的驗證對象,你可以通過CustomValidator自己創(chuàng)建一個。
?(5) 將用戶登錄名稱、密碼等數(shù)據(jù)加密保存。加密用戶輸入的數(shù)據(jù),然后再將它與數(shù)據(jù)庫中保存的數(shù)據(jù)比較,這相當(dāng)于對用戶輸入的數(shù)據(jù)進行了“消毒”處理,用戶輸入的數(shù)據(jù)不再對數(shù)據(jù)庫有任何特殊的意義,從而也就防止了攻擊者注入SQL命令
(6) 檢查提取數(shù)據(jù)的查詢所返回的記錄數(shù)量。如果程序只要求返回一個記錄,但實際返回的記錄卻超過一行,那就當(dāng)作出錯處理。
?(7)使用預(yù)處理語句
常用SQL注入的解決方案
1.添加圖形碼進行驗證
添加圖形碼在一定程序上增加代碼的安全性,給機器強制破解有一定的攔截作用,但不能阻止所有的攻擊,故還是需要在程序上進行安全性考慮
2.使用預(yù)備義語句和參數(shù)化查詢
使用預(yù)處理語句和參數(shù)化查詢。預(yù)處理語句和參數(shù)分別發(fā)送到數(shù)據(jù)庫服務(wù)器進行解析,參數(shù)將會被當(dāng)作普通字符處理。這種方式使得攻擊者無法注入惡意的SQL。常用的方式有兩種
2.1 使用PDO(PHP Data Objects )
$stmt = $pdo->prepare('SELECT * FROM t_admin WHERE admin_name = :name'); $stmt->execute(array('name' => $name)); foreach ($stmt as $row) {// do something with $row }注意,在默認情況使用PDO并沒有讓MySQL數(shù)據(jù)庫執(zhí)行真正的預(yù)處理語句(原因見下文)。為了解決這個問題,你應(yīng)該禁止PDO模擬預(yù)處理語句。一個正確使用PDO創(chuàng)建數(shù)據(jù)庫連接的例子如下
$dbConnection->setAttribute(PDO::ATTR_EMULATE_PREPARES, false); $dbConnection->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);2.2 使用MySQLi
$stmt = $dbConnection->prepare('SELECT * FROM t_admin WHERE admin_name = ?'); $stmt->bind_param('s', $name); $stmt->execute(); $result = $stmt->get_result(); while ($row = $result->fetch_assoc()) {// do something with $row }3.對用戶傳遞的參數(shù)進行處理
我們知道Web上提交數(shù)據(jù)有兩種方式,一種是get、一種是post,那么很多常見的sql注射就是從get方式入手的,而且注射的語句里面一定是包含一些sql語句的,因為沒有sql語句,那么如何進行,sql語句有四大句:select 、update、delete、insert,那么我們?nèi)绻谖覀兲峤坏臄?shù)據(jù)中進行過濾是不是能夠避免這些問題呢??
于是我們使用正則就構(gòu)建如下函數(shù):
那么我們就能夠進行校驗了,于是我們上面的程序代碼就變成了下面的:
<?php if (inject_check($_GET['id'])) { exit('你提交的數(shù)據(jù)非法,請檢查后重新提交!'); } else { $id = verify_id($_GET['id']); // 這里引用了我們的過濾函數(shù),對$id進行過濾 echo '提交的數(shù)據(jù)合法,請繼續(xù)!'; } ?>好,問題到這里似乎都解決了,但是我們有沒有考慮過post提交的數(shù)據(jù),大批量的數(shù)據(jù)呢??
比如一些字符可能會對數(shù)據(jù)庫造成危害,比如 ’ _ ‘, ’ %’,這些字符都有特殊意義,那么我們?nèi)绻M行控制呢?還有一點,就是當(dāng)我們的php.ini里面的magic_quotes_gpc = off的時候,那么提交的不符合數(shù)據(jù)庫規(guī)則的數(shù)據(jù)都是不會自動在前面加’ ‘的,那么我們要控制這些問題,于是構(gòu)建如下函數(shù):
對于WEB應(yīng)用來說,SQL注入攻擊無疑是首要防范的安全問題,系統(tǒng)底層對于數(shù)據(jù)安全方面本身進行了很多的處理和相應(yīng)的防范機制,例如:
即便用戶輸入了一些惡意的id參數(shù),系統(tǒng)也會強制轉(zhuǎn)換成整型,避免惡意注入。這是因為,系統(tǒng)會對數(shù)據(jù)進行強制的數(shù)據(jù)類型檢測,并且對數(shù)據(jù)來源進行數(shù)據(jù)格式轉(zhuǎn)換。而且,對于字符串類型的數(shù)據(jù),ThinkPHP都會進行escape_string處理(real_escape_string,mysql_escape_string),如果你采用PDO方式的話,還支持參數(shù)綁定。
通常的安全隱患在于你的查詢條件使用了字符串參數(shù),然后其中一些變量又依賴由客戶端的用戶輸入。
要有效的防止SQL注入問題,我們建議:
- 查詢條件盡量使用數(shù)組方式,這是更為安全的方式;
- 如果不得已必須使用字符串查詢條件,使用預(yù)處理機制;
- 使用自動驗證和自動完成機制進行針對應(yīng)用的自定義過濾;
- 如果環(huán)境允許,盡量使用PDO方式,并使用參數(shù)綁定。
查詢條件預(yù)處理
where方法使用字符串條件的時候,支持預(yù)處理(安全過濾),并支持兩種方式傳入預(yù)處理參數(shù),例如:
模型的query和execute方法 同樣支持預(yù)處理機制,例如:
execute方法用法同query方法。
總結(jié)
以上是生活随笔為你收集整理的php防止sql注入的方法的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Catalan数(数论)
- 下一篇: 为PHP7安装Windows Serve