因為PHP程序經常與HTML頁、Web地址(URL)以及數據庫交互,所以PHP提供一些函數來幫助你處理這些類型的數據。HTML、Web頁地址和數據庫命令都是字符串,但是它們每個都要求不同的字符以不同的方法來轉義。例如,在Web地址中一個空格被寫成%20,而直接量小於符號(<)在HTML文檔中必須寫作<。PHP有許多內置函數來轉換和取得這些編碼。
HTML:在HTML中特殊的字符以實體(entity)表示,如&和<。這里有兩個PHP函數來把字符串中的特殊字符轉換為實體,一個用於刪除HTML標簽,一個僅用於提取meta標簽。
對所有特殊字符進行實體引用:函數htmlentities()將HTML字符轉換為對應的實體(除了空格符)。包括小於符號(<)、大於符號(>)、與號(&)和重音字符。(entity實體)
只對HTML語法字符進行實體引用:函數htmlspacialchars()轉換最小的實體集來生成合法的HTML。下面的實體被轉換:
與符號(&)被轉換成&
雙引號(")被轉換成"
單引號(')被轉換成'(就像調用htmlentities()時使用ENT_QUOTES的效果)
小於號(<)被轉換成<
大於號(>)被轉換成>
如果有一個應用程序來顯示用戶填入表單的數據,則要在顯示和保存數據之前通過htmlspecialchars()處理數據。如果沒有處理的話,一旦用戶提交了像"angle<30"或"sturm & drang"這樣的字符串,瀏覽器會人為這些特殊的字符是HTML,從而得到一個混亂的頁面。
刪除HTML標簽
函數strip_tags()從字符串中刪除HTML標簽:
$input='<p>Howdy,"Cowboy"</p>;
$output=strip_tags($input);//$output是'Howdy,"Cowboy"'
函數可以有第二個參數來指定在字符串中留下的標簽。之列出標簽的開始形式,在第二個參數中列出的標簽結束形式也將被保留:
$input='The <b>bold</b> tags will<i>stay</i><p>';
$output=strip_tags($input,'<b>');//$output是'The <b>bold</b> tags will stay'
在保留標簽中的屬性不會被strip_tags()改變。由於HTML標簽屬性(例如style和onmouseover)可以影響Web頁面的外觀和行為,所以用strip_tags()保留一些標簽可能會導致無法刪除所有潛在的冗余內容。
提取元標簽
如果你把Web頁面的HTML存在一個字符串中,函數get_meta_tags()可返回包含該頁面中元標簽(meta tag)內容的數組。元標簽的名字(keywords、author、escription等)成為數組的鍵,而元標簽的內容則成為對應的值:
$meta_tags=get_meta_tags('http://www.example.com/');
echo "Web page made by {$meta_tags[author]}";
函數一般的形式是:
$array=get_meta_tags(filename[,use_include_path]);
可以指定參數use_include_path為true,這樣可使PHP嘗試用標准包含路徑打開文件。
URL:PHP提供了一些函數用於對URL進行編碼和解碼。有兩種方法對URL編碼,其區別在於如何處理空格。第一種(根據RFC1738規范)把空格當做URL中的另一個非法字符並把它編碼為%20。第二種(執行application/x-www-form-urlencoded系統)把空格編碼為一個+並且把它用於建立查詢的字符串中。
注意並不需要對一個完整的URL使用這些函數,例如http://www.example.com/hello,因為它們會轉義冒號和反斜杠:
http%3A%2F%2Fwww.example.com%2Fhello
應該只編碼部分URL(在http://www.example.com/后面的部分),隨后再加上協議和域名。
RFC1738編碼和解碼
要把字符串依照URL約定編碼,可以使用rawurlencode():
$output=rawurlencode(input);
該函數接收一個字符串並返回對該字符串的拷貝,該拷貝中把非法URL字符按%dd約定編碼。
如果你要為一個頁面里的鏈接動態生成超級鏈接地址,則需要用rawurlencode()轉換它們:
$name="Programming PHP";
$output=rawurlencode($name);
echo "http://localhost/$output";
http://localhost/Programming%20PHP
函數rawurldecode()用於解碼被編碼過的URL字符串:
$encoded='Programming%20PHP';
echo rawurldecode($encoded);
Programming PHP
查詢字符串編碼
urlencode()和urldecode()函數和它們原始版本(即rawurlencode()和rawurldecode())的不同僅在於它們把空格編碼為加號(+),而不是%20。這是用於創建查詢字符串(query string)和cookie值的格式,但是因為這些值在通過表單或cookie傳送時會自動解碼,所以你不需要使用這些函數來處理當前頁的查詢字符串或cookie。這兩個函數對於生成查詢字符串是很有用的:
$base_url='http://www.google.com/q=';
$query='PHP sessions -cookies';
$url=$base_url.urlencode($query);
echo $url;
http://www.google.com/q=PHP+sessions+cookies
SQL:絕大多數數據庫系統都要求將SQL查詢字符串進行轉義。SQL的轉義方法相當簡單——在單引號、雙引號、空字節和反斜杠前面加上一個反斜杠(\)。addslashes()函數可添加這些反斜杠,stripslashes()函數則刪除它們:
$string=<<<The_end
"It's never going to work," she cried,
as she hit the backslash(\)key.
The_End;
echo addslashes($string);
\"It\'s never going to work,\"she cried,
as she hit the backslash (\\) key.
echo stripslashes($string);
"It's never going to work," she cried,
as she hit the backslash (\) key.
提示:一些數據庫(如SYBASE)用另一個單引號為單引號轉義,而不是一個反斜杠。對於這些數據庫,可以在php.ini文件中打開magic_quotes_sybase。
數據錯誤的以代碼形式執行是漏洞的根本原因,那么修復方法當然是讓數據只能以數據的形式被SQL層認識並帶入查詢。
如果沒有用加反斜杠轉義,那么id=111'類似的查詢條件就會直接帶入查詢語句:select * from table where id='$id'就變成了select * from table where id='111'';查詢語句有未閉合的單引號報錯
而如果我們使用了函數addslashes()進行反斜杠轉義,那么SQL查詢語句會變成:select * from table where id='111\'',所以id=111'中的單引號由於被以\'的形式傳遞,所以會以值的形式執行,SQL就會認為id=111'中的單引號是id變量值的一部分,那么查詢結果有可能存在也有可能不存在,(例如,如果存在id為王二小's的數據庫值,就存在查詢結果),但是最重要的是,單引號在這里是以值的形式傳遞的,這才是最最根本的本質。
1. 對於PHP magic_quotes_gpc=on的情況,
我們可以不對輸入和輸出數據庫的字符串數據作,addslashes()和stripslashes()的操作,數據也會正常顯示。
如果此時你對輸入的數據作了addslashes()處理,那么在輸出的時候就必須使用stripslashes()去掉多余的反斜杠。
2. 對於PHP magic_quotes_gpc=off 的情況,必須使用addslashes()對輸入數據進行處理,但並不需要使用stripslashes()格式化輸出。因為addslashes()並未將反斜杠一起寫入數據庫,只是幫助mysql完成了sql語句的執行。
有的網站,外面是可以設置標簽為“編程's”等這種格式的,但是一點擊保存,就報錯了,特別搞。然后不管哪個訪問對應標簽的文章,都會看到這個報錯。這就是由於從用戶輸入到PHP代碼再到SQL層,其中PHP整合SQL查詢語句時的錯誤編寫造成的。
知識的積累過程,總是起先你不懂,而后學了一點,就顛兒顛兒的以為“兒得矣”,當有一天你突然迷茫了,覺得哪兒似乎沒那么形而上的通暢,然后你會不自主的想到底哪兒還有問題呢?你會迷茫很久,直到有一天終於找到了那個缺角,然后它是那么美好因為當你找到的時候就差不多已經有能力填充了。一切便豁然開朗。
有的時候,會略顯無奈,因為人們越來越浮躁,而安全環境剛建立,安全問題剛得到更多的重視,所以大多數人在“顛兒顛兒”的階段時,已經被業內大致認可,所以會特別盲目的自信,而有不少人,卻是玩了好久,也沒想到過還有哪兒值得迷茫。眼高手低,永遠是一個讓人崩潰的事實。
有的時候,id數據值會被設置為intval($id),這種情況下,如果變量只能是數字,就不會產生注入了,因為不管輸入什么都會以數字——及值的形式帶入數據庫查詢。然而,如果是字符型id值,而且還用到了addslashes(),是不是就安全了呢?,還真沒有,因為如果sql語句形式是select * from table where id="'$_POST[id]'",這種情況,id值被單引號包含,我們要想構造能以代碼執行的惡意值需要閉合前面的單引號,而addslashes會將我們輸入的單引號轉移為正常的值形式帶入查詢。這種情況就呵呵了。好在有的sql語句中參數值沒有用單引號包含,例如:select * from table where id="$_POST[id]",雖然開啟了addslashes轉義單引號等特殊字符,但是我們根本就不需要單引號閉合sql查詢語句中的id字段,所以照樣可以注入。