特殊字符轉義

Spring 不但提供了一個功能全面的應用開發框架,本身還擁有衆多可以在程序編寫時直接
使用的工具類,您不但可以在 Spring 應用中使用這些工具類,也可以在其它的應用中使用,這些工具類中的大部分是可以在脫離 Spring 框架時使用的。瞭解 Spring 中有哪些好用的工具類並在程序編寫時適當使用,將有助於提高開發效率、增強代碼質量。  
  
 在這個分爲兩部分的文章中,我們將從衆多的 Spring 工具類中遴選出那些好用的工具類介紹給大家。第 1 部分 介紹了與文件資源操作和 Web 相關的工具類。在第 2 部分中將介紹特殊字符轉義和方法入參檢測工具類。  
  
 特殊字符轉義  
  
 由於 Web 應用程序需要聯合使用到多種語言,每種語言都包含一些特殊的字符,對於動態語言或標籤式的語言而言,如果需要動態構造語言的內容時,一個我們經常會碰到的問題就是特殊字符轉義的問題。下面是 Web 開發者最常面對需要轉義的特殊字符類型:  
  
 HTML 特殊字符;   
 JavaScript 特殊字符;   
 SQL 特殊字符;   
 如果不對這些特殊字符進行轉義處理,則不但可能破壞文檔結構,還可以引發潛在的安全問題。 Spring 爲 HTML 和 JavaScript 特殊字符提供了轉義操作工具類,它們分別是 HtmlUtils 和 JavaScriptUtils。  
  
 HTML 特殊字符轉義  
  
 HTML 中 <,>,& 等字符有特殊含義,它們是 HTML 語言的保留字,因此不能直接使用。使用這些個字符時,應使用它們的轉義序列:  
  
 &:&   
 " :"   
 < :<   
 > :>   
 由於 HTML 網頁本身就是一個文本型結構化文檔,如果直接將這些包含了 HTML 特殊字符的內容輸出到網頁中,極有可能破壞整個 HTML 文檔的結構。所以,一般情況下需要對動態數據進行轉義處理,使用轉義序列表示 HTML 特殊字符。下面的 JSP 網頁將一些變量動態輸出到 HTML 網頁中:  
  
  
 清單 1. 未進行 HTML 特殊字符轉義處理網頁  
                   
 <%@ page language="java" contentType="text/html; charset=utf-8"%>  
 <%!  
    String userName = "</td><tr></table>";  
    String address = " \" type=\"button";  
  %>  
 <table border="1">  
    <tr>  
      <td>姓名:</td><td><%=userName%>< /td> ①  
    </tr>  
    <tr>  
      <td>年齡:</td><td>28</td>  
    </tr>  
 </table>  
  <input value="<%=address%>"  type="text" /> ②  
    
  
  
 在 ① 和 ② 處,我們未經任何轉義處理就直接將變量輸出到 HTML 網頁中,由於這些變量可能包含一些特殊的 HTML 的字符,它們將可能破壞整個 HTML 文檔的結構。我們可以從以上 JSP 頁面的一個具體輸出中瞭解這一問題:  
  
 <table border="1">  
    <tr>  
      <td>姓名:</td><td>< /td><tr></table></td>   
      ① 破壞了 <table> 的結構  
    </tr>  
    <tr>  
      <td>年齡:</td><td>28</td>  
    </tr>  
 </table>  
  <input value=" " type="button"  type="text" />   
  ② 將本來是輸入框組件偷樑換柱爲按鈕組件  
    
  
  
 融合動態數據後的 HTML 網頁已經面目全非,首先 ① 處的 <table> 結構被包含 HTML 特殊字符的 userName 變量截斷了,造成其後的 <table> 代碼變成無效的內容;其次,② 處 <input> 被動態數據改換爲按鈕類型的組件(type="button")。爲了避免這一問題,我們需要事先對可能破壞 HTML 文檔結構的動態數據進行轉義處理。Spring 爲我們提供了一個簡單適用的 HTML 特殊字符轉義工具類,它就是 HtmlUtils。下面,我們通過一個簡單的例子瞭解 HtmlUtils 的具體用法:  
  
  
 清單 2. HtmpEscapeExample  
                   
 package com.baobaotao.escape;  
 import org.springframework.web.util.HtmlUtils;  
 public class HtmpEscapeExample {  
     public static void main(String[] args) {  
         String specialStr = "<div id=\"testDiv\">test1;test2</div>";  
         String str1 = HtmlUtils.htmlEscape(specialStr); ①轉換爲 HTML轉義字符表示  
         System.out.println(str1);  
          
         String str2 = HtmlUtils.htmlEscapeDecimal(specialStr); ② 轉換爲數據轉義表示  
         System.out.println(str2);  
          
         String str3 = HtmlUtils.htmlEscapeHex(specialStr); ③轉換爲十六進制數據轉義表示  
         System.out.println(str3);  
          
         ④下面對轉義後字符串進行反向操作  
         System.out.println(HtmlUtils.htmlUnescape(str1));  
         System.out.println(HtmlUtils.htmlUnescape(str2));  
         System.out.println(HtmlUtils.htmlUnescape(str3));  
     }  
 }  
    
  
  
 HTML 不但可以使用通用的轉義序列表示 HTML 特殊字符,還可以使用以 # 爲前綴的數字序列表示 HTML 特殊字符,它們在最終的顯示效果上是一樣的。HtmlUtils 提供了三個轉義方法:  
  
 方法 說明   
 static String htmlEscape(String input)  將 HTML 特殊字符轉義爲 HTML 通用轉義序列;   
 static String htmlEscapeDecimal(String input)  將 HTML 特殊字符轉義爲帶 # 的十進制數據轉義序列;   
 static String htmlEscapeHex(String input)  將 HTML 特殊字符轉義爲帶 # 的十六進制數據轉義序列;   
  
 此外,HtmlUtils 還提供了一個能夠將經過轉義內容還原的方法:htmlUnescape(String input),它可以還原以上三種轉義序列的內容。運行以上代碼,您將可以看到以下的輸出:  
  
 str1:<div id="testDiv">test1;test2</div>  
 str2:<div id="testDiv">test1;test2</div>  
 str3:<div id="testDiv">test1;test2</div>  
 <div id="testDiv">test1;test2</div>  
 <div id="testDiv">test1;test2</div>  
 <div id="testDiv">test1;test2</div>  
    
  
  
 您只要使用 HtmlUtils 對代碼 清單 1 的 userName 和 address 進行轉義處理,最終輸出的 HTML 頁面就不會遭受破壞了。  
  
 JavaScript 特殊字符轉義  
  
 JavaScript 中也有一些需要特殊處理的字符,如果直接將它們嵌入 JavaScript 代碼中,JavaScript 程序結構將會遭受破壞,甚至被嵌入一些惡意的程序。下面列出了需要轉義的特殊 JavaScript 字符:  
  
 ' :\'   
 " :\"   
 \ :\\   
 走紙換頁: \f   
 換行:\n   
 換欄符:\t   
 回車:\r   
 回退符:\b   
    
 我們通過一個具體例子演示動態變量是如何對 JavaScript 程序進行破壞的。假設我們有一個 JavaScript 數組變量,其元素值通過一個 Java List 對象提供,下面是完成這一操作的 JSP 代碼片斷:  
  
  
 清單 3. jsTest.jsp:未對 JavaScript 特殊字符進行處理  
                   
 <%@ page language="java" contentType="text/html; charset=utf-8"%>  
 <jsp:directive.page import="java.util.*"/>  
 <%  
   List textList = new ArrayList();  
   textList.add("\";alert();j=\"");  
 %>  
 <script>  
   var txtList = new Array();  
    <% for ( int i = 0 ; i < textList.size() ; i++) { %>  
      txtList[<%=i%>] = "<%=textList.get(i)%>";   
      ① 未對可能包含特殊 JavaScript 字符的變量進行處理  
    <% } %>  
 </script>  
    
  
  
 當客戶端調用這個 JSP 頁面後,將得到以下的 HTML 輸出頁面:  
  
 <script>  
   var txtList = new Array();  
    txtList[0] = "";alert();j=""; ① 本來是希望接受一個字符串,結果被植入了一段JavaScript代碼  
 </script>  
    
  
  
 由於包含 JavaScript 特殊字符的 Java 變量直接合併到 JavaScript 代碼中,我們本來期望 ① 處所示部分是一個普通的字符串,但結果變成了一段 JavaScript 代碼,網頁將彈出一個 alert 窗口。想像一下如果粗體部分的字符串是“";while(true)alert();j="”時會產生什麼後果呢?  
  
 因此,如果網頁中的 JavaScript 代碼需要通過拼接 Java 變量動態產生時,一般需要對變量的內容進行轉義處理,可以通過 Spring 的 JavaScriptUtils 完成這件工作。下面,我們使用 JavaScriptUtils 對以上代碼進行改造:  
  
 <%@ page language="java" contentType="text/html; charset=utf-8"%>  
 <jsp:directive.page import="java.util.*"/>  
 <jsp:directive.page import="org.springframework.web.util.JavaScriptUtils"/>  
 <%  
   List textList = new ArrayList();  
   textList.add("\";alert();j=\"");  
 %>  
 <script>  
    var txtList = new Array();  
    <% for ( int i = 0 ; i < textList.size() ; i++) { %>  
    ① 在輸出動態內容前事先進行轉義處理  
    txtList[<%=i%>] = "<%=JavaScriptUtils.javaScriptEscape(""+textList.get(i))%>";  
    <% } %>  
 </script>  
    
  
  
 通過轉義處理後,這個 JSP 頁面輸出的結果網頁的 JavaScript 代碼就不會產生問題了:  
  
 <script>  
    var txtList = new Array();  
    txtList[0] = "\";alert();j=\"";  
    ① 粗體部分僅是一個普通的字符串,而非一段 JavaScript 的語句了  
 </script>  
    
  
  
 SQL特殊字符轉義  
  
 應該說,您即使沒有處理 HTML 或 JavaScript 的特殊字符,也不會帶來災難性的後果,但是如果不在動態構造 SQL 語句時對變量中特殊字符進行處理,將可能導致程序漏洞、數據盜取、數據破壞等嚴重的安全問題。網絡中有大量講解 SQL 注入的文章,感興趣的讀者可以搜索相關的資料深入研究。  
  
 雖然 SQL 注入的後果很嚴重,但是隻要對動態構造的 SQL 語句的變量進行特殊字符轉義處理,就可以避免這一問題的發生了。來看一個存在安全漏洞的經典例子:  
  
 SELECT COUNT(userId)   
 FROM t_user   
 WHERE userName='"+userName+"' AND password ='"+password+"';  
    
  
  
 以上 SQL 語句根據返回的結果數判斷用戶提供的登錄信息是否正確,如果 userName 變量不經過特殊字符轉義處理就直接合併到 SQL 語句中,黑客就可以通過將 userName 設置爲 “1' or '1'='1”繞過用戶名/密碼的檢查直接進入系統了。  
  
 所以除非必要,一般建議通過 PreparedStatement 參數綁定的方式構造動態 SQL 語句,因爲這種方式可以避免 SQL 注入的潛在安全問題。但是往往很難在應用中完全避免通過拼接字符串構造動態 SQL 語句的方式。爲了防止他人使用特殊 SQL 字符破壞 SQL 的語句結構或植入惡意操作,必須在變量拼接到 SQL 語句之前對其中的特殊字符進行轉義處理。Spring 並沒有提供相應的工具類,您可以通過 jakarta commons lang 通用類包中(spring/lib/jakarta-commons/commons- lang.jar)的 StringEscapeUtils 完成這一工作:  
  
  
 清單 4. SqlEscapeExample  
                   
 package com.baobaotao.escape;  
 import org.apache.commons.lang.StringEscapeUtils;  
 public class SqlEscapeExample {  
     public static void main(String[] args) {  
         String userName = "1' or '1'='1";  
         String password = "123456";  
         userName = StringEscapeUtils.escapeSql(userName);  
         password = StringEscapeUtils.escapeSql(password);  
         String sql = "SELECT COUNT(userId) FROM t_user WHERE userName='" 
             + userName + "' AND password ='" + password + "'";  
         System.out.println(sql);  
     }  
 }  
    
  
  
 事實上,StringEscapeUtils 不但提供了 SQL 特殊字符轉義處理的功能,還提供了 HTML、XML、 JavaScript、Java 特殊字符的轉義和還原的方法。如果您不介意引入 jakarta commons lang 類包,我們更推薦您使用 StringEscapeUtils 工具類完成特殊字符轉義處理的工作。    http://qiuyujia.iteye.com/blog/841122
發佈了49 篇原創文章 · 獲贊 15 · 訪問量 20萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章