XSS漏洞
XSS漏洞
攻擊者利用XSS(Cross-site scripting)漏洞攻擊可以在用戶的瀏覽器中執行JS惡意腳本,XSS攻擊可以實現用戶會話劫持、釣魚攻擊、惡意重定向、點擊劫持、掛馬、XSS蠕蟲等,XSS攻擊類型分為:反射型、存儲型、DOM型。
1. 反射型XSS攻擊
示例 - 存在反射型XSS的xss.jsp代碼:
<%=request.getParameter("input")%>
攻擊者通過傳入惡意的input參數值可以在用戶瀏覽器中注入一段JavaScript腳本。
示例 - 注入XSS代碼:
<script>alert('xss');</script>
瀏覽器請求:http://localhost:8000/modules/servlet/xss.jsp?input=%3Cscript%3Ealert(%27xss%27)%3B%3C/script%3E

2. 存儲型XSS攻擊
示例 - 存在存儲型XSS的guestbook.jsp代碼:
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ page import="java.text.SimpleDateFormat" %>
<%@ page import="java.util.*" %>
<%
String username = request.getParameter("username");
String content = request.getParameter("content");
String guestBookKey = "GUEST_BOOK";
List<Map<String, String>> comments = new ArrayList<Map<String, String>>();
if (content != null) {
Object obj = application.getAttribute(guestBookKey);
if (obj != null) {
comments = (List<Map<String, String>>) obj;
}
Map<String, String> comment = new HashMap<String, String>();
String ip = request.getHeader("x-real-ip");
if (ip == null) {
ip = request.getRemoteAddr();
}
comment.put("username", username);
comment.put("content", content);
comment.put("ip", ip);
comment.put("date", new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
comments.add(comment);
application.setAttribute(guestBookKey, comments);
}
%>
<html>
<head>
<title>留言板</title>
</head>
<style>
* {
margin: 0;
padding: 0;
}
</style>
<body>
<div style="border: 1px solid #C6C6C6;">
<div style="text-align: center;">
<h2>在線留言板</h2>
</div>
<div>
<dl>
<%
Object obj = application.getAttribute(guestBookKey);
if (obj instanceof List) {
comments = (List<Map<String, String>>) obj;
for (Map<String, String> comment : comments) {
%>
<dd>
<div style="min-height: 50px; margin: 20px; border-bottom: 1px solid #9F9F9F;">
<p><B><%=comment.get("username")%>
</B>[<%=comment.get("ip")%>] 于 <%=comment.get("date")%> 發表回復:</p>
<p style="margin: 15px 0 5px 0; font-size: 12px;">
<pre><%=comment.get("content")%></pre>
</p>
</div>
</dd>
<%
}
}
%>
</dl>
</div>
<div style="background-color: #fff; border: 1px solid #C6C6C6;">
<form action="#" method="POST" style="margin: 20px;">
昵稱: <input type="text" name="username" style="width:250px; height: 28px;"/><br/><br/>
<textarea name="content" style="overflow: auto;width: 100%; height: 250px;"></textarea>
<input type="submit" value="提交留言" style="margin-top: 20px; width: 80px; height: 30px;"/>
</form>
</div>
</div>
</body>
</html>
訪問:http://10.10.99.2:8000/modules/servlet/guestbook.jsp,并在留言內容出填入xss測試代碼,如下:

提交留言后頁面會刷新,并執行留言的xss代碼:

3. DOM XSS
示例 - dom.jsp代碼:
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
Date: <span style="color: red;"></span>
<input type="hidden" value="<%=request.getParameter("date")%>" />
<script>
var date = document.getElementsByTagName("input")[0].value;
document.getElementsByTagName("span")[0].innerHTML = date;
</script>
正常請求測試:http://localhost:8000/modules/servlet/dom.jsp?date=2020-11-15%2015:57:22

XSS攻擊測試:http://localhost:8000/modules/servlet/dom.jsp?date=%3Cimg%20src=1%20onerror=alert(/xss/)%20/%3E

4. XSS防御
XSS最為常見的處理方式是轉義特殊字符,后端程序在接受任何用戶輸入的參數時都應當優先考慮是否會存在XSS攻擊。
4.1 htmlspecialchars
在PHP中通常會使用htmlspecialchars函數會將一些可能有攻擊威脅的字符串轉義為html實體編碼,這樣可以有效的避免XSS攻擊。
*示例 - htmlspecialchars 轉義: *
| 字符 | 替換后 |
|---|---|
& (& 符號) |
& |
" (雙引號) |
" |
' (單引號) |
'或者' |
< (小于) |
< |
> (大于) |
> |
在Java中雖然沒有內置如此簡單方便的函數,但是我們可以通過字符串替換的方式實現類似htmlspecialchars函數的功能。
/**
* 實現htmlSpecialChars函數把一些預定義的字符轉換為HTML實體編碼
*
* @param content 輸入的字符串內容
* @return HTML實體化轉義后的字符串
*/
public static String htmlSpecialChars(String content) {
if (content == null) {
return null;
}
char[] charArray = content.toCharArray();
StringBuilder sb = new StringBuilder();
for (char c : charArray) {
switch (c) {
case '&':
sb.append("&");
break;
case '"':
sb.append(""");
break;
case '\'':
sb.append("'");
break;
case '<':
sb.append("<");
break;
case '>':
sb.append(">");
break;
default:
sb.append(c);
break;
}
}
return sb.toString();
}
在存儲或者輸出請求參數的時候使用該方法過濾即可實現XSS防御。
4.2 全局的XSSFilter
package com.anbai.sec.vuls.filter;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.io.IOException;
public class XSSFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) {
}
@Override
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
// 創建HttpServletRequestWrapper,包裝原HttpServletRequest對象,示例程序只重寫了getParameter方法,
// 應當考慮如何過濾:getParameter、getParameterValues、getParameterMap、getInputStream、getReader
HttpServletRequestWrapper requestWrapper = new HttpServletRequestWrapper(request) {
public String getParameter(String name) {
// 獲取參數值
String value = super.getParameter(name);
// 簡單轉義參數值中的特殊字符
return value.replace("&", "&").replace("<", "<").replace("'", "'");
}
};
chain.doFilter(requestWrapper, resp);
}
@Override
public void destroy() {
}
}
web.xml添加XSSFilter過濾器:
<!-- XSS過濾器 -->
<filter>
<filter-name>XSSFilter</filter-name>
<filter-class>com.anbai.sec.vuls.filter.XSSFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>XSSFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
請求XSS示例程序:http://localhost:8000/modules/servlet/xss.jsp?input=%3Cscript%3Ealert(%27xss%27);%3C/script%3E

經過全局過濾器轉義后的參數就不會再帶有XSS攻擊能力了。
4.3 RASP XSS攻擊防御
RASP可以實現類似于全局XSSFilter的請求參數過濾功能,比較穩定的一種方式是Hook到javax.servlet.ServletRequest接口的實現類的getParameter/getParameterValues/getParameterMap等核心方法,在該方法return之后插入RASP的檢測代碼。這種實現方案雖然麻煩,但是可以避免觸發Http請求參數解析問題(Web應用無法獲取getInputStream和亂碼等問題)。
示例 - RASP對getParameter返回值Hook示例:

反射型的XSS防御相對來說比較簡單,直接禁止GET參數中出現<>標簽,只要出現就理解攔截,如:
http://localhost:8000/modules/servlet/xss.jsp?input=<script>alert('xss');</script>
過濾或攔截掉<>后input參數就不再具有攻擊性了。
但是POST請求的XSS參數就沒有那么容易過濾了,為了兼顧業務,不能簡單的使用htmlSpecialChars的方式直接轉義特殊字符,因為很多時候應用程序是必須支持HTML標簽的(如:<img>、<h1>等)。RASP在防御XSS攻擊的時候應當盡可能的保證用戶的正常業務不受影響,否則可能導致用戶無法業務流程阻塞或崩潰。
為了支持一些常用的HTML標簽和HTML標簽屬性,RASP可以通過詞法解析的方式,將傳入的字符串參數值解析成HTML片段,然后分析其中的標簽和屬性是否合法即可。

4.4 RASP XSS防御能力測試
4.4.1 惡意的HTML標簽屬性XSS測試
示例 - 提交帶有XSS攻擊的Payload:
<img src='1.jpg' width='10px' height='10px' onerror='alert(/xss/);' />
請求示例地址:http://localhost:8000/modules/servlet/guestbook.jsp,并填寫XSS攻擊代碼,如下圖:

RASP能夠正確識別并攔截XSS攻擊:

4.4.2 XSS富文本檢測測試
RASP如果要實現精確的XSS檢測能力就必須能夠正確的識別出用戶傳入的數據到底是否合法,經過HTML詞法分析后RASP能夠正確認識用戶傳入的參數值是否是包含了惡意的HTML標簽或者屬性。
示例 - 用戶在留言板中帶圖片回復:

示例 - 用戶在留言板中回復被注釋的HTML片段:


經測試,RASP對XSS攻擊防御能力正常,能夠識別合法的HTML和javascript代碼(DOM類XSS占不支持)。
Java Web安全
推薦文章: