<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    隨筆 - 63  文章 - 0  trackbacks - 0
    <2009年4月>
    2930311234
    567891011
    12131415161718
    19202122232425
    262728293012
    3456789

    常用鏈接

    留言簿(2)

    隨筆分類

    隨筆檔案

    搜索

    •  

    最新評(píng)論

    閱讀排行榜

    評(píng)論排行榜

    特殊字符轉(zhuǎn)義

    由于 Web 應(yīng)用程序需要聯(lián)合使用到多種語言,每種語言都包含一些特殊的字符,對(duì)于動(dòng)態(tài)語言或標(biāo)簽式的語言而言,如果需要?jiǎng)討B(tài)構(gòu)造語言的內(nèi)容時(shí),一個(gè)我們經(jīng)常會(huì)碰到的問題就是特殊字符轉(zhuǎn)義的問題。下面是 Web 開發(fā)者最常面對(duì)需要轉(zhuǎn)義的特殊字符類型:

    • HTML 特殊字符;
    • JavaScript 特殊字符;
    • SQL 特殊字符;

    如果不對(duì)這些特殊字符進(jìn)行轉(zhuǎn)義處理,則不但可能破壞文檔結(jié)構(gòu),還可以引發(fā)潛在的安全問題。Spring 為 HTML 和 JavaScript 特殊字符提供了轉(zhuǎn)義操作工具類,它們分別是 HtmlUtils 和 JavaScriptUtils。

    HTML 特殊字符轉(zhuǎn)義

    HTML 中 <,>,& 等字符有特殊含義,它們是 HTML 語言的保留字,因此不能直接使用。使用這些個(gè)字符時(shí),應(yīng)使用它們的轉(zhuǎn)義序列:

    • &:&amp;
    • " :&quot;
    • < :&lt;
    • > :&gt;

    由于 HTML 網(wǎng)頁本身就是一個(gè)文本型結(jié)構(gòu)化文檔,如果直接將這些包含了 HTML 特殊字符的內(nèi)容輸出到網(wǎng)頁中,極有可能破壞整個(gè) HTML 文檔的結(jié)構(gòu)。所以,一般情況下需要對(duì)動(dòng)態(tài)數(shù)據(jù)進(jìn)行轉(zhuǎn)義處理,使用轉(zhuǎn)義序列表示 HTML 特殊字符。下面的 JSP 網(wǎng)頁將一些變量動(dòng)態(tài)輸出到 HTML 網(wǎng)頁中:


    清單 1. 未進(jìn)行 HTML 特殊字符轉(zhuǎn)義處理網(wǎng)頁
                <%@ 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" /> ②
                

    在 ① 和 ② 處,我們未經(jīng)任何轉(zhuǎn)義處理就直接將變量輸出到 HTML 網(wǎng)頁中,由于這些變量可能包含一些特殊的 HTML 的字符,它們將可能破壞整個(gè) HTML 文檔的結(jié)構(gòu)。我們可以從以上 JSP 頁面的一個(gè)具體輸出中了解這一問題:

    <table border="1">
                <tr>
                <td>姓名:</td><td></td><tr></table></td>
                ① 破壞了 <table> 的結(jié)構(gòu)
                </tr>
                <tr>
                <td>年齡:</td><td>28</td>
                </tr>
                </table>
                <input value=" " type="button"  type="text" />
                ② 將本來是輸入框組件偷梁換柱為按鈕組件
                

    融合動(dòng)態(tài)數(shù)據(jù)后的 HTML 網(wǎng)頁已經(jīng)面目全非,首先 ① 處的 <table> 結(jié)構(gòu)被包含 HTML 特殊字符的 userName 變量截?cái)嗔耍斐善浜蟮?<table> 代碼變成無效的內(nèi)容;其次,② 處 <input> 被動(dòng)態(tài)數(shù)據(jù)改換為按鈕類型的組件(type="button")。為了避免這一問題,我們需要事先對(duì)可能破壞 HTML 文檔結(jié)構(gòu)的動(dòng)態(tài)數(shù)據(jù)進(jìn)行轉(zhuǎn)義處理。Spring 為我們提供了一個(gè)簡(jiǎn)單適用的 HTML 特殊字符轉(zhuǎn)義工具類,它就是 HtmlUtils。下面,我們通過一個(gè)簡(jiǎn)單的例子了解 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); ①轉(zhuǎn)換為HTML轉(zhuǎn)義字符表示
                System.out.println(str1);
                String str2 = HtmlUtils.htmlEscapeDecimal(specialStr); ②轉(zhuǎn)換為數(shù)據(jù)轉(zhuǎn)義表示
                System.out.println(str2);
                String str3 = HtmlUtils.htmlEscapeHex(specialStr); ③轉(zhuǎn)換為十六進(jìn)制數(shù)據(jù)轉(zhuǎn)義表示
                System.out.println(str3);
                ④下面對(duì)轉(zhuǎn)義后字符串進(jìn)行反向操作
                System.out.println(HtmlUtils.htmlUnescape(str1));
                System.out.println(HtmlUtils.htmlUnescape(str2));
                System.out.println(HtmlUtils.htmlUnescape(str3));
                }
                }
                

    HTML 不但可以使用通用的轉(zhuǎn)義序列表示 HTML 特殊字符,還可以使用以 # 為前綴的數(shù)字序列表示 HTML 特殊字符,它們?cè)谧罱K的顯示效果上是一樣的。HtmlUtils 提供了三個(gè)轉(zhuǎn)義方法:

    方法 說明
    static String htmlEscape(String input) 將 HTML 特殊字符轉(zhuǎn)義為 HTML 通用轉(zhuǎn)義序列;
    static String htmlEscapeDecimal(String input) 將 HTML 特殊字符轉(zhuǎn)義為帶 # 的十進(jìn)制數(shù)據(jù)轉(zhuǎn)義序列;
    static String htmlEscapeHex(String input) 將 HTML 特殊字符轉(zhuǎn)義為帶 # 的十六進(jìn)制數(shù)據(jù)轉(zhuǎn)義序列;

    此外,HtmlUtils 還提供了一個(gè)能夠?qū)⒔?jīng)過轉(zhuǎn)義內(nèi)容還原的方法:htmlUnescape(String input),它可以還原以上三種轉(zhuǎn)義序列的內(nèi)容。運(yùn)行以上代碼,您將可以看到以下的輸出:

    str1:&lt;div id=&quot;testDiv&quot;&gt;test1;test2&lt;/div&gt;
                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 對(duì)代碼 清單 1 的 userName 和 address 進(jìn)行轉(zhuǎn)義處理,最終輸出的 HTML 頁面就不會(huì)遭受破壞了。

    JavaScript 特殊字符轉(zhuǎn)義

    JavaScript 中也有一些需要特殊處理的字符,如果直接將它們嵌入 JavaScript 代碼中,JavaScript 程序結(jié)構(gòu)將會(huì)遭受破壞,甚至被嵌入一些惡意的程序。下面列出了需要轉(zhuǎn)義的特殊 JavaScript 字符:

    • ' :\'
    • " :\"
    • \ :\\
    • 走紙換頁: \f
    • 換行:\n
    • 換欄符:\t
    • 回車:\r
    • 回退符:\b
    ?

    我們通過一個(gè)具體例子演示動(dòng)態(tài)變量是如何對(duì) JavaScript 程序進(jìn)行破壞的。假設(shè)我們有一個(gè) JavaScript 數(shù)組變量,其元素值通過一個(gè) Java List 對(duì)象提供,下面是完成這一操作的 JSP 代碼片斷:


    清單 3. jsTest.jsp:未對(duì) JavaScript 特殊字符進(jìn)行處理
                <%@ 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)%>";
                ① 未對(duì)可能包含特殊 JavaScript 字符的變量進(jìn)行處理
                <% } %>
                </script>
                

    當(dāng)客戶端調(diào)用這個(gè) JSP 頁面后,將得到以下的 HTML 輸出頁面:

    <script>
                var txtList = new Array();
                txtList[0] = "";alert();j=""; ① 本來是希望接受一個(gè)字符串,結(jié)果被植入了一段JavaScript代碼
                </script>
                

    由于包含 JavaScript 特殊字符的 Java 變量直接合并到 JavaScript 代碼中,我們本來期望 ① 處所示部分是一個(gè)普通的字符串,但結(jié)果變成了一段 JavaScript 代碼,網(wǎng)頁將彈出一個(gè) alert 窗口。想像一下如果粗體部分的字符串是“";while(true)alert();j="”時(shí)會(huì)產(chǎn)生什么后果呢?

    因此,如果網(wǎng)頁中的 JavaScript 代碼需要通過拼接 Java 變量動(dòng)態(tài)產(chǎn)生時(shí),一般需要對(duì)變量的內(nèi)容進(jìn)行轉(zhuǎn)義處理,可以通過 Spring 的 JavaScriptUtils 完成這件工作。下面,我們使用 JavaScriptUtils 對(duì)以上代碼進(jìn)行改造:

    <%@ 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++) { %>
                ① 在輸出動(dòng)態(tài)內(nèi)容前事先進(jìn)行轉(zhuǎn)義處理
                txtList[<%=i%>] = "<%=JavaScriptUtils.javaScriptEscape(""+textList.get(i))%>";
                <% } %>
                </script>
                

    通過轉(zhuǎn)義處理后,這個(gè) JSP 頁面輸出的結(jié)果網(wǎng)頁的 JavaScript 代碼就不會(huì)產(chǎn)生問題了:

    <script>
                var txtList = new Array();
                txtList[0] = "\";alert();j=\"";
                ① 粗體部分僅是一個(gè)普通的字符串,而非一段 JavaScript 的語句了
                </script>
                

    SQL特殊字符轉(zhuǎn)義

    應(yīng)該說,您即使沒有處理 HTML 或 JavaScript 的特殊字符,也不會(huì)帶來災(zāi)難性的后果,但是如果不在動(dòng)態(tài)構(gòu)造 SQL 語句時(shí)對(duì)變量中特殊字符進(jìn)行處理,將可能導(dǎo)致程序漏洞、數(shù)據(jù)盜取、數(shù)據(jù)破壞等嚴(yán)重的安全問題。網(wǎng)絡(luò)中有大量講解 SQL 注入的文章,感興趣的讀者可以搜索相關(guān)的資料深入研究。

    雖然 SQL 注入的后果很嚴(yán)重,但是只要對(duì)動(dòng)態(tài)構(gòu)造的 SQL 語句的變量進(jìn)行特殊字符轉(zhuǎn)義處理,就可以避免這一問題的發(fā)生了。來看一個(gè)存在安全漏洞的經(jīng)典例子:

    SELECT COUNT(userId)
                FROM t_user
                WHERE userName='"+userName+"' AND password ='"+password+"';
                

    以上 SQL 語句根據(jù)返回的結(jié)果數(shù)判斷用戶提供的登錄信息是否正確,如果 userName 變量不經(jīng)過特殊字符轉(zhuǎn)義處理就直接合并到 SQL 語句中,黑客就可以通過將 userName 設(shè)置為 “1' or '1'='1”繞過用戶名/密碼的檢查直接進(jìn)入系統(tǒng)了。

    所以除非必要,一般建議通過 PreparedStatement 參數(shù)綁定的方式構(gòu)造動(dòng)態(tài) SQL 語句,因?yàn)檫@種方式可以避免 SQL 注入的潛在安全問題。但是往往很難在應(yīng)用中完全避免通過拼接字符串構(gòu)造動(dòng)態(tài) SQL 語句的方式。為了防止他人使用特殊 SQL 字符破壞 SQL 的語句結(jié)構(gòu)或植入惡意操作,必須在變量拼接到 SQL 語句之前對(duì)其中的特殊字符進(jìn)行轉(zhuǎn)義處理。Spring 并沒有提供相應(yīng)的工具類,您可以通過 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);
                }
                }
                

    事實(shí)上,StringEscapeUtils 不但提供了 SQL 特殊字符轉(zhuǎn)義處理的功能,還提供了 HTML、XML、JavaScript、Java 特殊字符的轉(zhuǎn)義和還原的方法。如果您不介意引入 jakarta commons lang 類包,我們更推薦您使用 StringEscapeUtils 工具類完成特殊字符轉(zhuǎn)義處理的工作。





    回頁首


    方法入?yún)z測(cè)工具類

    Web 應(yīng)用在接受表單提交的數(shù)據(jù)后都需要對(duì)其進(jìn)行合法性檢查,如果表單數(shù)據(jù)不合法,請(qǐng)求將被駁回。類似的,當(dāng)我們?cè)诰帉戭惖姆椒〞r(shí),也常常需要對(duì)方法入?yún)⑦M(jìn)行合法性檢查,如果入?yún)⒉环弦螅椒▽⑼ㄟ^拋出異常的方式拒絕后續(xù)處理。舉一個(gè)例子:有一個(gè)根據(jù)文件名獲取輸入流的方法:InputStream getData(String file),為了使方法能夠成功執(zhí)行,必須保證 file 入?yún)⒉荒転?null 或空白字符,否則根本無須進(jìn)行后繼的處理。這時(shí)方法的編寫者通常會(huì)在方法體的最前面編寫一段對(duì)入?yún)⑦M(jìn)行檢測(cè)的代碼,如下所示:

    public InputStream getData(String file) {
                if (file == null || file.length() == 0|| file.replaceAll("\\s", "").length() == 0) {
                throw new IllegalArgumentException("file入?yún)⒉皇怯行У奈募刂?);
                }
                …
                }
                

    類似以上檢測(cè)方法入?yún)⒌拇a是非常常見,但是在每個(gè)方法中都使用手工編寫檢測(cè)邏輯的方式并不是一個(gè)好主意。閱讀 Spring 源碼,您會(huì)發(fā)現(xiàn) Spring 采用一個(gè) org.springframework.util.Assert 通用類完成這一任務(wù)。

    Assert 翻譯為中文為“斷言”,使用過 JUnit 的讀者都熟知這個(gè)概念,它斷定某一個(gè)實(shí)際的運(yùn)行值和預(yù)期想一樣,否則就拋出異常。Spring 對(duì)方法入?yún)⒌臋z測(cè)借用了這個(gè)概念,其提供的 Assert 類擁有眾多按規(guī)則對(duì)方法入?yún)⑦M(jìn)行斷言的方法,可以滿足大部分方法入?yún)z測(cè)的要求。這些斷言方法在入?yún)⒉粷M足要求時(shí)就會(huì)拋出 IllegalArgumentException。下面,我們來認(rèn)識(shí)一下 Assert 類中的常用斷言方法:

    斷言方法 說明
    notNull(Object object) 當(dāng) object 不為 null 時(shí)拋出異常,notNull(Object object, String message) 方法允許您通過 message 定制異常信息。和 notNull() 方法斷言規(guī)則相反的方法是 isNull(Object object)/isNull(Object object, String message),它要求入?yún)⒁欢ㄊ?null;
    isTrue(boolean expression) / isTrue(boolean expression, String message) 當(dāng) expression 不為 true 拋出異常;
    notEmpty(Collection collection) / notEmpty(Collection collection, String message) 當(dāng)集合未包含元素時(shí)拋出異常。notEmpty(Map map) / notEmpty(Map map, String message) 和 notEmpty(Object[] array, String message) / notEmpty(Object[] array, String message) 分別對(duì) Map 和 Object[] 類型的入?yún)⑦M(jìn)行判斷;
    hasLength(String text) / hasLength(String text, String message) 當(dāng) text 為 null 或長(zhǎng)度為 0 時(shí)拋出異常;
    hasText(String text) / hasText(String text, String message) text 不能為 null 且必須至少包含一個(gè)非空格的字符,否則拋出異常;
    isInstanceOf(Class clazz, Object obj) / isInstanceOf(Class type, Object obj, String message) 如果 obj 不能被正確造型為 clazz 指定的類將拋出異常;
    isAssignable(Class superType, Class subType) / isAssignable(Class superType, Class subType, String message) subType 必須可以按類型匹配于 superType,否則將拋出異常;

    使用 Assert 斷言類可以簡(jiǎn)化方法入?yún)z測(cè)的代碼,如 InputStream getData(String file) 在應(yīng)用 Assert 斷言類后,其代碼可以簡(jiǎn)化為以下的形式:

    public InputStream getData(String file){
                Assert.hasText(file,"file入?yún)⒉皇怯行У奈募刂?);
                ① 使用 Spring 斷言類進(jìn)行方法入?yún)z測(cè)
                …
                }
                

    可見使用 Spring 的 Assert 替代自編碼實(shí)現(xiàn)的入?yún)z測(cè)邏輯后,方法的簡(jiǎn)潔性得到了不少的提高。Assert 不依賴于 Spring 容器,您可以大膽地在自己的應(yīng)用中使用這個(gè)工具類。





    回頁首


    小結(jié)

    本文介紹了一些常用的 Spring 工具類,其中大部分 Spring 工具類不但可以在基于 Spring 的應(yīng)用中使用,還可以在其它的應(yīng)用中使用。

    對(duì)于 Web 應(yīng)用來說,由于有很多關(guān)聯(lián)的腳本代碼,如果這些代碼通過拼接字符串的方式動(dòng)態(tài)產(chǎn)生,就需要對(duì)動(dòng)態(tài)內(nèi)容中特殊的字符進(jìn)行轉(zhuǎn)義處理,否則就有可能產(chǎn)生意想不到的后果。Spring 為此提供了 HtmlUtils 和 JavaScriptUtils 工具類,只要將動(dòng)態(tài)內(nèi)容在拼接之前使用工具類進(jìn)行轉(zhuǎn)義處理,就可以避免類似問題的發(fā)生了。如果您不介意引入一個(gè)第三方類包,那么 jakarta commons lang 通用類包中的 StringEscapeUtils 工具類可能更加適合,因?yàn)樗峁┝烁尤娴霓D(zhuǎn)義功能。

    最后我們還介紹了 Spring 的 Assert 工具類,Assert 工具類是通用性很強(qiáng)的工具類,它使用面向?qū)ο蟮姆绞浇鉀Q方法入?yún)z測(cè)的問題,您可以在自己的應(yīng)用中使用 Assert 對(duì)方法入?yún)⑦M(jìn)行檢查。


    posted on 2009-04-05 08:45 lanxin1020 閱讀(184) 評(píng)論(0)  編輯  收藏 所屬分類: spring
    主站蜘蛛池模板: 亚洲一区在线免费观看| 亚洲中文字幕AV每天更新| 99久久免费精品高清特色大片| 亚洲第一页在线播放| 日韩成全视频观看免费观看高清| 日本激情猛烈在线看免费观看 | 亚洲成人精品久久| 成人男女网18免费视频| 国产伦精品一区二区免费| 亚洲精品国产成人中文| 国产a级特黄的片子视频免费| 久久免费美女视频| 亚洲国产成人久久精品大牛影视| 亚洲国产一二三精品无码| 成人人免费夜夜视频观看| 中文字幕乱码一区二区免费| 亚洲а∨精品天堂在线| 337p欧洲亚洲大胆艺术| www.亚洲精品| 24小时免费直播在线观看| 最近中文字幕免费大全| 综合偷自拍亚洲乱中文字幕 | 亚洲天堂电影在线观看| 4338×亚洲全国最大色成网站| 久久午夜免费视频| 黄网站免费在线观看| 老司机午夜免费视频| 一本色道久久综合亚洲精品蜜桃冫| 亚洲成色WWW久久网站| 免费一级做a爰片性色毛片| 日韩吃奶摸下AA片免费观看| 免费高清国产视频| 国产精品免费一区二区三区| 亚洲真人无码永久在线观看| 久久99亚洲网美利坚合众国| 亚洲色精品vr一区二区三区 | 日韩亚洲人成在线| 亚洲日产2021三区在线| 亚洲av无码成h人动漫无遮挡 | 香港特级三A毛片免费观看| 2019亚洲午夜无码天堂|