一個反射型XSS例子的解析
阿新 • • 發佈:2019-01-02
我們在訪問一個網頁的時候,在URL後面加上引數,伺服器根據請求的引數值構造不同的HTML返回。
如http://localhost:8080/prjWebSec/xss/reflectedXSS.jsp?param=value...
上例中的value可能出現在返回的HTML(可能是JS,HTML某元素的內容或者屬性)中,
如果將value改成可以在瀏覽器中被解釋執行的東西,就形成了反射型XSS.
有人會問,我怎麼可能自己去把value改成可以執行的惡意程式碼呢?這不是自己坑自己嗎.
但是一種情況是別人可能修改這個value值,然後將這個惡意的URL傳送給你,或者別人,當URL地址被開啟時,
特有的惡意程式碼引數被HTML解析,執行.它的特點是非持久化,必須使用者點選帶有特定引數的連結才能引起.
瀏覽器輸出original: value
但是如果URL改成 http://localhost:8080/prjWebSec/xss/reflectedXSS.jsp?param=value';alert('x')//
瀏覽器會先alert,然後輸出original: value.
檢視瀏覽器的原始碼可以看到:var scriptVar='value';alert('x')//';
當value';alert('x')//被返回給瀏覽器的時候var scriptVar='<%=param%>';變成了
var scriptVar='value';alert('x')//';
這就是一個簡單的反射型XSS例項.
下面我們來看怎麼防止這種XSS.commons-lang和OWASP的ESAPI都提供了工具類。
http://localhost:8080/prjWebSec/xss/reflectedXSS.jsp?param=value中文';alert('x')//<>
進行unicode編碼(\u+十六進位制).
ESAPI.encoder().encodeForJavaScript會對所有非數字和非英文字元的字元進行編碼,對寬位元組字元
進行unicode編碼,對其他字元進行\x+十六進位制編碼。
瀏覽器執行JavaScript的時候會解釋轉意和解碼成字元.相當於自動呼叫了JavaScripte的unescape方法.
通過escapeJavaScript和encodeForJavaScript可以避免輸出到JavaScript的內容被當做JavaScript執行。
那下面這個URL會發生什麼事情呢?
http://localhost:8080/prjWebSec/xss/reflectedXSS.jsp?param=1中文';alert('x')//<img [email protected] onError="javascript:alert('error')">
會彈出一個alert('x')和3次alert('error'),同時DOM裡被添加了3個
<img onerror="javascript:alert('error')" src="@">
這是什麼原因造成的呢?alert('x')的執行還是因為沒有對Javascript的元素進行編碼.
另外3個alert('error')是因為html內容沒有經過html編碼,在DOM裡插入了3個img元素,取不到src指定的
圖片,從而觸發了onerror事件.
解決的方法是如果要將返回資料做為HTML節點的內容,一定要保證內容被真正的當做資料來解釋,
而不要被解釋成html元素。
StringEscapeUtils.escapeHtml和ESAPI.encoder().encodeForHTML可以幫助我們完成這個功能.
下面的程式碼既保證了不被當做Javascript指令碼,也保證了不被解釋成HTML元素.
如http://localhost:8080/prjWebSec/xss/reflectedXSS.jsp?param=value...
上例中的value可能出現在返回的HTML(可能是JS,HTML某元素的內容或者屬性)中,
如果將value改成可以在瀏覽器中被解釋執行的東西,就形成了反射型XSS.
有人會問,我怎麼可能自己去把value改成可以執行的惡意程式碼呢?這不是自己坑自己嗎.
但是一種情況是別人可能修改這個value值,然後將這個惡意的URL傳送給你,或者別人,當URL地址被開啟時,
特有的惡意程式碼引數被HTML解析,執行.它的特點是非持久化,必須使用者點選帶有特定引數的連結才能引起.
下面來看一個簡單的例子:
當用戶通過URL http://localhost:8080/prjWebSec/xss/reflectedXSS.jsp?param=value訪問的時候,utilits.js: function writeToDom(str){ document.writeln(str); } function writelnToDom(str){ document.writeln(str + "<br>"); } reflectedXSS.jsp: <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %> <%@ page import="org.apache.commons.lang.StringEscapeUtils"%> <%@ page import="java.net.URLDecoder,java.net.URLEncoder"%> <%@ page import="org.owasp.esapi.ESAPI"%> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>test XSS</title> <script type="text/javascript" src="../js/utilits.js"></script> </head> <% String param = request.getParameter("param"); System.out.println("original " + param); %> <script> var scriptVar='<%=param%>'; writelnToDom("original: " + scriptVar); </script> <body> </body> </html>
瀏覽器輸出original: value
但是如果URL改成 http://localhost:8080/prjWebSec/xss/reflectedXSS.jsp?param=value';alert('x')//
瀏覽器會先alert,然後輸出original: value.
檢視瀏覽器的原始碼可以看到:var scriptVar='value';alert('x')//';
當value';alert('x')//被返回給瀏覽器的時候var scriptVar='<%=param%>';變成了
var scriptVar='value';alert('x')//';
這就是一個簡單的反射型XSS例項.
下面我們來看怎麼防止這種XSS.commons-lang和OWASP的ESAPI都提供了工具類。
以這個URL來測試<% String param = request.getParameter("param"); System.out.println("original " + param); String secparam = StringEscapeUtils.escapeJavaScript(request.getParameter("param")); System.out.println("StringEscapeUtils " + secparam); String owaspparam = ESAPI.encoder().encodeForJavaScript(request.getParameter("param")); System.out.println("OWASP " + owaspparam); out.write("server side output ------------------------------------------------------- "); out.write("<br>original: " + param); out.write("<br>StringEscapeUtils: " + secparam); out.write("<br>OWASP: " + owaspparam); %> <script> writelnToDom("<br> client side output---------------------------------------------"); var scriptVar='<%=param%>'; writelnToDom("original: " + scriptVar); var secVar='<%=secparam%>'; writelnToDom('StringEscapeUtils:' + secVar); var owaspparam='<%=owaspparam%>'; writelnToDom("OWASP: " + owaspparam); </script>
http://localhost:8080/prjWebSec/xss/reflectedXSS.jsp?param=value中文';alert('x')//<>
system.out的輸出為:
original value中文';alert('x')//<>
StringEscapeUtils value\u4E2D\u6587\';alert(\'x\')//<>
OWASP value\u4E2D\u6587\x27\x3Balert\x28\x27x\x27\x29\x2F\x2F\x3C\x3E
瀏覽器會alert一次,同時輸出下面的內容server side output -------------------------------------------------------
original: value中文';alert('x')//<>
StringEscapeUtils: value\u4E2D\u6587\';alert(\'x\')//<>
OWASP: value\u4E2D\u6587\x27\x3Balert\x28\x27x\x27\x29\x2F\x2F\x3C\x3E
client side output---------------------------------------------
original: value中文
StringEscapeUtils:value中文';alert('x')//<>
OWASP: value中文';alert('x')//<>
StringEscapeUtils.escapeJavaScript會對單引號'和雙引號"前面加上轉意符(\),對寬位元組字元進行unicode編碼(\u+十六進位制).
ESAPI.encoder().encodeForJavaScript會對所有非數字和非英文字元的字元進行編碼,對寬位元組字元
進行unicode編碼,對其他字元進行\x+十六進位制編碼。
瀏覽器執行JavaScript的時候會解釋轉意和解碼成字元.相當於自動呼叫了JavaScripte的unescape方法.
通過escapeJavaScript和encodeForJavaScript可以避免輸出到JavaScript的內容被當做JavaScript執行。
那下面這個URL會發生什麼事情呢?
http://localhost:8080/prjWebSec/xss/reflectedXSS.jsp?param=1中文';alert('x')//<img [email protected] onError="javascript:alert('error')">
會彈出一個alert('x')和3次alert('error'),同時DOM裡被添加了3個
<img onerror="javascript:alert('error')" src="@">
這是什麼原因造成的呢?alert('x')的執行還是因為沒有對Javascript的元素進行編碼.
另外3個alert('error')是因為html內容沒有經過html編碼,在DOM裡插入了3個img元素,取不到src指定的
圖片,從而觸發了onerror事件.
解決的方法是如果要將返回資料做為HTML節點的內容,一定要保證內容被真正的當做資料來解釋,
而不要被解釋成html元素。
StringEscapeUtils.escapeHtml和ESAPI.encoder().encodeForHTML可以幫助我們完成這個功能.
下面的程式碼既保證了不被當做Javascript指令碼,也保證了不被解釋成HTML元素.
<%
String doubleSecparam = StringEscapeUtils.escapeJavaScript(
StringEscapeUtils.escapeHtml(request.getParameter("param")));
String doubleOwasp = ESAPI.encoder().encodeForJavaScript(
ESAPI.encoder().encodeForHTML(request.getParameter("param")));
%>
<script>
var doubleScriptVar='<%=doubleSecparam%>';
writelnToDom("doubleSecparam StringEscapeUtils: " + doubleScriptVar);
var doubleOwasp='<%=doubleOwasp%>';
writelnToDom("Double OWASP: " + doubleOwasp);
</script>
檢視瀏覽器的原始碼,我們發現html元素會被編碼成html entity
var doubleScriptVar='1中文\';alert(\'x\')//<img
[email protected] onError="javascript:alert(\'error\')">';
var doubleOwasp='1\x26\x23x4e2d\x3B\x26\x23x6587\x3B\x26\x23x27\x3B\x26\x23x3b\x3Balert\x26
\x23x28\x3B\x26\x23x27\x3Bx\x26\x23x27\x3B\x26\x23x29\x3B\x26\x23x2f\x3B\x26\x23x2f\x3B
\x26lt\x3Bimg\x20src\x26\x23x3d\x3B\x26\x23x40\x3B\x20onError\x26\x23x3d\x3B
\x26quot\x3Bjavascript\x26\x23x3a\x3Balert\x26\x23x28\x3B\x26\x23x27\x3Berror
\x26\x23x27\x3B\x26\x23x29\x3B\x26quot\x3B\x26gt\x3B';
當然,現實過程中,很少有網站有如此明顯的xss漏洞.這裡只是給大家示範了一下反射型xss的原理,現實中的漏洞雖然五花八門,但是本質是不變的.兩個有用的連結
http://blog.csdn.net/langyan666/article/details/5524479
http://lcamtuf.coredump.cx/