PHPSQL注入

什麼是SQL注入?

就是經過把SQL命令插入到Web表單遞交或輸入域名或頁面請求的查詢字符串,最終達到欺騙服務器執行惡意的SQL命令。 
例如一個簡單的登陸表單(這裏把密碼寫成明文方便說明): 
這裏寫圖片描述 
當在表單中填寫這樣的語句進行提交登陸時會出現這樣的SQL語句php

select * from t_admin where admin_name='xxx' and admin_pwd='xxx'' or '1'

這樣會查詢出全部的用戶信息,全部存在不安全隱患html

經常使用的SQL注入方式

下面看看一些經常使用例測試的SQL注入語句。mysql

1. 使用單引號及or關鍵字
SELECT * FROM Users WHERE Username='$username' AND Password='$password' 


咱們針對上面的SQL語句分析,發現若是用下面的測試數據就可以進行SQL注入了 
引用 
username=1or1=1password=1’or’1’=’1 

看看整個SQL查詢語句變成: 
引用
程序員

SELECT * FROM Users WHERE Username='1' OR '1'='1' AND Password='1'OR '1'='1' 


假設參數值是經過GET方法傳遞到服務器的,且域名爲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語句做簡單分析後咱們就知道因爲該語句永遠爲真,因此確定會返回一些數據,在這種狀況下實際上並未驗證用戶名和密碼,而且在某些系統中,用戶表的第一行記錄是管理員,那這樣形成的後果則更爲嚴重。sql

2. 使用括號



另一個查詢的例子以下: 引用 
數據庫

SELECT * FROM Users WHERE((Username='$username')AND(Password=MD5('$password'))) 


在這個例子中,存在兩個問題,一個是括號的用法,還有一個是MD5哈希函數的用法。對於第一個問題,咱們很容找出缺乏的右括號解決,對於第二個問題,咱們能夠想辦法使第二個條件失效。咱們在查詢語句的最後加上一個註釋符以表示後面的都是註釋,常見的註釋起始符是/*(在Oracle中是–),也就是說,咱們用以下的用戶名和密碼: 
引用 
username=1or1=1))/password = foo 

那麼整條SQL語句就變爲: 
引用
數組

SELECT * FROM Users WHERE(( Username='1'or '1'='1'))/*')AND (Password=MD5('$password'))) 



那麼看看URL請求就變爲: 
引用 
http://www.example.com/index.php?username=1‘%20or%20’1’%20=%20’1’))/*&password=foo 

#####3.Union查詢SQL注入測試 
 


Union查詢SQL注入測試 
 還有一種測試是利用Union的,利用Union能夠鏈接查詢,從而從其餘表中獲得信息,假設以下查詢: 
引用安全

SELECT Name, Phone, Address FROM Users WHERE Id=$id 


而後咱們設置id的值爲: 
引用 

$id =1 UNION ALL SELECT creditCardNumber,1,1 FROM CreditCarTable 

那麼總體的查詢就變爲: 
引用服務器

SELECT Name, Phone,Address FROM Users WHERE Id=1 UNION ALL SELECT creaditCardNumber,1,1 FROM CreditCarTable 


顯示這就能獲得全部信用卡用戶的信息。 

盲目SQL注入測試 
 在上面咱們提到過盲SQL注入,即bind SQL Injection,它意味着對於某個操做咱們得不到任何信息,一般這是因爲程序員已經編寫了特定的出錯返回頁面,從而隱藏了數據庫結構的信息。 

 但利用推理方法,有時候咱們可以恢復特定字段的值。這種方法一般採用一組對服務器的布爾查詢,依據返回的結果來推斷結果的含義。仍然延續上面的www.example.com有一個參數名爲id, 那麼咱們輸入如下url請求: 
引用 

 http://www.exampe.com/index.php?id=1’ 

顯然因爲語法錯誤,咱們會獲得一個預先定義好的出錯頁面,假設服務器上的查詢語句爲 
引用 

SELECT field1,field2,field3 FROM Users WHERE Id=’Id使SUBSTRING(text,start,length),ASCII(char),LENGTH(text)idId=1’ AND ASCII(SUBSTRING(username,1,1))=97 AND ‘1’=’1 

那麼最終的SQL查詢語句爲: 
引用 
框架

SELECT field1,field2,field3 FROM Users WHERE Id='1' AND ASCII(SUBSTRING(username,1,1))=97 AND '1'='1' 


那麼,若是在數據庫中有用戶名的第一字符的ASCII碼爲97的話,那麼咱們就能獲得一個真值u,那麼就繼續尋找該用戶名的下一個字符;若是沒有的話,那麼咱們就增猜想第一個字符的ASCII碼爲98的用戶名,這樣反覆下去就能判斷出合法的用戶名

經常使用SQL注入的解決方案

1.添加圖形碼進行驗證

添加圖形碼在必定程序上增長代碼的安全性,給機器強制破解有必定的攔截做用,但不能阻止全部的攻擊,故仍是須要在程序上進行安全性考慮

2.使用預備義語句和參數化查詢

使用預處理語句和參數化查詢。預處理語句和參數分別發送到數據庫服務器進行解析,參數將會被看成普通字符處理。這種方式使得攻擊者沒法注入惡意的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數據庫執行真正的預處理語句(緣由見下文)。爲了解決這個問題,你應該禁止PDO模擬預處理語句。一個正確使用PDO建立數據庫鏈接的例子以下

$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.對用戶傳遞的參數進行處理

咱們知道Web上提交數據有兩種方式,一種是get、一種是post,那麼不少常見的sql注射就是從get方式入手的,並且注射的語句裏面必定是包含一些sql語句的,由於沒有sql語句,那麼如何進行,sql語句有四大句:select 、update、delete、insert,那麼咱們若是在咱們提交的數據中進行過濾是否是可以避免這些問題呢? 
因而咱們使用正則就構建以下函數:

<?php function inject_check($sql_str) { return eregi('select|insert|update|delete|'); } function verify_id($id=null) { if (!$id) { exit('沒有提交參數!'); } // 是否爲空判斷 elseif (inject_check($id)) { exit('提交的參數非法!'); } // 注射判斷 elseif (!is_numeric($id)) { exit('提交的參數非法!'); } // 數字判斷 $id = intval($id); // 整型化 return $id; } ?>

那麼咱們就可以進行校驗了,因而咱們上面的程序代碼就變成了下面的:

<?php if (inject_check($_GET['id'])) { exit('你提交的數據非法,請檢查後從新提交!'); } else { $id = verify_id($_GET['id']); // 這裏引用了咱們的過濾函數,對$id進行過濾 echo '提交的數據合法,請繼續!'; } ?>

好,問題到這裏彷佛都解決了,可是咱們有沒有考慮過post提交的數據,大批量的數據呢? 
好比一些字符可能會對數據庫形成危害,好比 ’ _ ‘, ’ %’,這些字符都有特殊意義,那麼咱們若是進行控制呢?還有一點,就是當咱們的php.ini裏面的magic_quotes_gpc = off的時候,那麼提交的不符合數據庫規則的數據都是不會自動在前面加’ ‘的,那麼咱們要控制這些問題,因而構建以下函數:

<?php function str_check( $str ) { if (!get_magic_quotes_gpc()) // 判斷magic_quotes_gpc是否打開 { $str = addslashes($str); // 進行過濾 } $str = str_replace("_", "\_", $str); // 把 '_'過濾掉 $str = str_replace("%", "\%", $str); // 把' % '過濾掉 return $str; } ?>
最後,再考慮提交一些大批量數據的狀況,好比發貼,或者寫文章、新聞,咱們須要一些函數來幫咱們過濾和進行轉換,再上面函數的基礎上,對提交的數據進行引號轉義及將HTML標籤轉換成HTML實體,能夠構建以下函數:
<?php function post_check($post) { if (!get_magic_quotes_gpc()) // 判斷magic_quotes_gpc是否爲打開 { $post = addslashes($post); // 進行magic_quotes_gpc沒有打開的狀況對提交數據的過濾 } $post = str_replace("_", "\_", $post); // 把 '_'過濾掉 $post = str_replace("%", "\%", $post); // 把' % '過濾掉 $post = nl2br($post); // 回車轉換 $post= htmlspecialchars($post); // html標記轉換 return $post; } ?>

或者根據API文檔中的:
TP框架防止sql注入:

對於WEB應用來講,SQL注入攻擊無疑是首要防範的安全問題,系統底層對於數據安全方面自己進行了不少的處理和相應的防範機制,例如:

  1. $User = M("User"); // 實例化User對象
  2. $User->find($_GET["id"]);

即使用戶輸入了一些惡意的id參數,系統也會強制轉換成整型,避免惡意注入。這是由於,系統會對數據進行強制的數據類型檢測,而且對數據來源進行數據格式轉換。並且,對於字符串類型的數據,ThinkPHP都會進行escape_string處理(real_escape_string,mysql_escape_string),若是你採用PDO方式的話,還支持參數綁定。

一般的安全隱患在於你的查詢條件使用了字符串參數,而後其中一些變量又依賴由客戶端的用戶輸入。

要有效的防止SQL注入問題,咱們建議:

  • 查詢條件儘可能使用數組方式,這是更爲安全的方式;
  • 若是不得已必須使用字符串查詢條件,使用預處理機制;
  • 使用自動驗證和自動完成機制進行鍼對應用的自定義過濾;
  • 若是環境容許,儘可能使用PDO方式,並使用參數綁定

查詢條件預處理

where方法使用字符串條件的時候,支持預處理(安全過濾),並支持兩種方式傳入預處理參數,例如:

  1. $Model->where("id=%d and username='%s' and xx='%f'",array($id,$username,$xx))->select();
  2. // 或者
  3. $Model->where("id=%d and username='%s' and xx='%f'",$id,$username,$xx)->select();

模型的query和execute方法 一樣支持預處理機制,例如:

  1. $model->query('select * from user where id=%d and status=%d',$id,$status);
  2. //或者
  3. $model->query('select * from user where id=%d and status=%d',array($id,$status));

execute方法用法同query方法。

相關文章
相關標籤/搜索