XSS問題描述
跨站腳本(Cross site script,簡稱xss)是一種“HTML注入”,由於攻擊的腳本多數時候是跨域的,所以稱之爲“跨域腳本”。
我們常常聽到“注入”(Injection),如SQL注入,那麼到底“注入”是什麼?注入本質上就是把輸入的數據變成可執行的程序語句。SQL注入是如此,XSS也如此,只不過XSS一般注入的是惡意的腳本代碼,這些腳本代碼可以用來獲取合法用戶的數據,如Cookie信息。
或者當訪問者瀏覽網頁時惡意代碼會被執行或者通過給管理員發信息的方式誘使管理員瀏覽,從而獲得管理員權限,控制整個網站
攻擊者利用跨站請求僞造能夠輕鬆地強迫用戶的瀏覽器發出非故意的HTTP請求,如詐騙性的電匯請求、修改口令和下載非法的內容等請求。
風險等級:高危
XSS風險分析
該漏洞可能被攻擊者利用竊取或操縱客戶會話和Cookie,它們可能用於模仿合法用戶,從而使黑客能夠以合法用戶身份登錄系統進行滲透。可執行攻擊者惡意腳本。
XSS 注入方法
在 HTML 中內嵌的文本中,惡意內容以 script 標籤形成注入。
在內聯的 JavaScript 中,拼接的數據突破了原本的限制(字符串,變量,方法名等)。
在標籤屬性中,惡意內容包含引號,從而突破屬性值的限制,注入其他屬性或者標籤。
在標籤的 href、src 等屬性中,包含
javascript:
等可執行代碼。在 onload、onerror、onclick 等事件中,注入不受控制代碼。
在 style 屬性和標籤中,包含類似
background-image:url("javascript:…");
的代碼(新版本瀏覽器已經可以防範)。在 style 屬性和標籤中,包含類似
expression(…)
的 CSS 表達式代碼(新版本瀏覽器已經可以防範)。
XSS 防禦
本文基於JavaWeb後端考慮,前端可參考: 前端安全系列:如何防止XSS攻擊?:
依賴jar
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>3.0-alpha-1</version>
<scope>provided</scope>
</dependency>
web.xml添加過濾器
<!-- 解決xss漏洞 -->
<filter>
<filter-name>xssFilter</filter-name>
<filter-class>com.newcapec.cloudpay.filter.XssFilter</filter-class>
</filter>
<!-- 解決xss漏洞 -->
<filter-mapping>
<filter-name>xssFilter</filter-name>
<!--過濾路徑-->
<url-pattern>*</url-pattern>
</filter-mapping>
過濾器
自定義過濾器Filter攔截請求,並對請求參數進行xss過濾處理
package com.newcapec.cloudpay.filter;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
/**
* @Title: XSS過濾器
* @ClassName: com.newcapec.cloudpay.filter.XssFilter.java
* @Description:
*
* @Copyright 2016-2018 Powered By 研發中心
* @author: 王延飛
* @date: 2019-01-24 16:22
* @version V1.0
*/
public class XssFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
//使用包裝器
XSSRequestWrapper XSSRequestWrapper=new XSSRequestWrapper((HttpServletRequest) servletRequest);
filterChain.doFilter(XSSRequestWrapper,servletResponse);
}
@Override
public void destroy() {
}
}
過濾器包裝器
package com.newcapec.cloudpay.filter;
import org.apache.commons.lang.StringUtils;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
/**
* @version V1.0
* @Title: Request的包裝類
* @ClassName: com.newcapec.cloudpay.filter.XSSRequestWrapper.java
* @Description: XSSRequestWrapper是Request的包裝類, 用於修改Request請求,這是攔截器Interceptor所不能做到的
* @Copyright 2016-2018 Powered By 研發中心
* @author: 王延飛
* @date: 2019-01-25 9:15
*/
public class XSSRequestWrapper extends HttpServletRequestWrapper {
public XSSRequestWrapper(HttpServletRequest request) {
super(request);
}
/**
* @param name
* @return java.lang.String[]
* @Title: 對數組參數進行特殊字符過濾
* @methodName: getParameterValues
* @Description:
* @author: 王延飛
* @date: 2019-01-25 10:23
*/
@Override
public String[] getParameterValues(String name) {
String[] values = super.getParameterValues(name);
if (values == null) {
return null;
}
int count = values.length;
String[] encodedValues = new String[count];
for (int i = 0; i < count; i++) {
encodedValues[i] = clearXss(values[i]);
}
return encodedValues;
}
/**
* @param name
* @return java.lang.String
* @Title: 對參數中特殊字符進行過濾
* @methodName: getParameter
* @Description:
* @author: 王延飛
* @date: 2019-01-25 10:23
*/
@Override
public String getParameter(String name) {
String value = super.getParameter(name);
if (value == null) {
return null;
}
return clearXss(value);
}
/**
* @param name
* @return java.lang.Object
* @Title: 獲取attribute, 特殊字符過濾
* @methodName: getAttribute
* @Description:
* @author: 王延飛
* @date: 2019-01-25 10:24
*/
@Override
public Object getAttribute(String name) {
Object value = super.getAttribute(name);
if (value != null && value instanceof String) {
clearXss((String) value);
}
return value;
}
/**
* @Title: 對請求頭部進行特殊字符過濾
* @methodName: getHeader
* @param name
* @return java.lang.String
* @Description:
*
* @author: 王延飛
* @date: 2019-01-25 10:34
*/
/*@Override
public String getHeader(String name) {
String value = super.getHeader(name);
if (value == null) {
return null;
}
return clearXss(value);
}*/
/**
* @param value
* @return java.lang.String
* @Title: 特殊字符處理(轉義或刪除)
* @methodName: clearXss
* @Description:
* @author: 王延飛
* @date: 2019-01-25 9:16
*/
private String clearXss(String value) {
if (StringUtils.isEmpty(value)) {
return value;
}
// 字符編碼不一致,需要轉換。我們系統是UTF-8編碼,這裏不需要
/*try {
value = new String(value.getBytes("ISO8859-1"), "UTF-8");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
return null;
}*/
return XssFilterUtil.stripXss(value);
}
}
過濾器工具類
package com.newcapec.cloudpay.filter;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* @version V1.0
* @Title:
* @ClassName: com.newcapec.cloudpay.filter.XssFilterUtil.java
* @Description:
* @Copyright 2016-2018 Powered By 研發中心
* @author: 王延飛
* @date: 2019-01-25 9:11
*/
public class XssFilterUtil {
private static final Logger log = LoggerFactory.getLogger(XssFilterUtil.class);
private static List<Pattern> patterns = null;
/**
* @param
* @return java.util.List<java.lang.Object [ ]>
* @Title: XSS常見攻擊
* @methodName: getXssPatternList
* @Description: Pattern.MULTILINE(? m):在這種模式下,'^'和'$'分別匹配一行的開始和結束。此外,'^'仍然匹配字符串的開始,'$'也匹配字符串的結束。
* 默認情況下,這兩個表達式僅僅匹配字符串的開始和結束。
* <p>
* Pattern.DOTALL(?s) :在這種模式下,表達式'.'可以匹配任意字符,包括表示一行的結束符。
* 默認情況下,表達式'.'不匹配行的結束符。
* @author: 王延飛
* @date: 2019-01-25 9:11
*/
private static List<Object[]> getXssPatternList() {
List<Object[]> ret = new ArrayList<Object[]>();
ret.add(new Object[]{"<(no)?script[^>]*>.*?</(no)?script>", Pattern.CASE_INSENSITIVE});
ret.add(new Object[]{"</script>", Pattern.CASE_INSENSITIVE});
ret.add(new Object[]{"<script(.*?)>", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL});
ret.add(new Object[]{"eval\\((.*?)\\)", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL});
ret.add(new Object[]{"expression\\((.*?)\\)", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL});
ret.add(new Object[]{"(javascript:|vbscript:|view-source:)*", Pattern.CASE_INSENSITIVE});
ret.add(new Object[]{"<(\"[^\"]*\"|\'[^\']*\'|[^\'\">])*>", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL});
ret.add(new Object[]{"(window\\.location|window\\.|\\.location|document\\.cookie|document\\.|alert\\(.*?\\)|window\\.open\\()*", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL});
ret.add(new Object[]{"<+\\s*\\w*\\s*(oncontrolselect|oncopy|oncut|ondataavailable|ondatasetchanged|ondatasetcomplete|ondblclick|ondeactivate|ondrag|ondragend|ondragenter|ondragleave|ondragover|ondragstart|ondrop|onerror=|onerroupdate|onfilterchange|onfinish|onfocus|onfocusin|onfocusout|onhelp|onkeydown|onkeypress|onkeyup|onlayoutcomplete|onload|onlosecapture|onmousedown|onmouseenter|onmouseleave|onmousemove|onmousout|onmouseover|onmouseup|onmousewheel|onmove|onmoveend|onmovestart|onabort|onactivate|onafterprint|onafterupdate|onbefore|onbeforeactivate|onbeforecopy|onbeforecut|onbeforedeactivate|onbeforeeditocus|onbeforepaste|onbeforeprint|onbeforeunload|onbeforeupdate|onblur|onbounce|oncellchange|onchange|onclick|oncontextmenu|onpaste|onpropertychange|onreadystatechange|onreset|onresize|onresizend|onresizestart|onrowenter|onrowexit|onrowsdelete|onrowsinserted|onscroll|onselect|onselectionchange|onselectstart|onstart|onstop|onsubmit|onunload)+\\s*=+", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL});
return ret;
}
/**
* @param
* @return java.util.List<java.util.regex.Pattern>
* @Title: XSS常見攻擊-正則表達式
* @methodName: getPatterns
* @Description:
* @author: 王延飛
* @date: 2019-01-25 9:11
*/
private static List<Pattern> getPatterns() {
if (patterns == null) {
List<Pattern> list = new ArrayList<Pattern>();
String regex = null;
Integer flag = null;
int arrLength = 0;
for (Object[] arr : getXssPatternList()) {
arrLength = arr.length;
for (int i = 0; i < arrLength; i++) {
regex = (String) arr[0];
flag = (Integer) arr[1];
list.add(Pattern.compile(regex, flag));
}
}
patterns = list;
}
return patterns;
}
/**
* @param value
* @return java.lang.String
* @Title: 處理特殊字符
* @methodName: stripXss
* @Description: 如果是特殊字符,策略有兩種:轉義或刪除
* @author: 王延飛
* @date: 2019-01-25 9:12
*/
public static String stripXss(String value) {
if (StringUtils.isNotBlank(value)) {
log.info("【XSS攻擊防禦】,接收字符是:{}", value);
//
Matcher matcher = null;
for (Pattern pattern : getPatterns()) {
matcher = pattern.matcher(value);
// 匹配
if (matcher.find()) {
// 刪除相關字符串
value = matcher.replaceAll("");
}
}
log.info("【XSS攻擊防禦】,匹配正則是:{},處理後是:{}", matcher, value);
/**
* 替換爲轉移字符,類似HtmlUtils.htmlEscape
*/
//value = value.replaceAll("<", "<").replaceAll(">", ">");
//刪除特殊符號
//String specialStr = "%20|=|!=|-|--|;|'|\"|%|#|+|//|/| |\\|<|>|(|)|{|}";
if (StringUtils.isNotBlank(value)) {
String specialStr = "%20|=|!=|-|--|;|'|\"|%|#|[+]|//|/| |\\|<|>|(|)|{|}";
for (String str : specialStr.split("\\|")) {
if (value.indexOf(str) > -1) {
value = value.replaceAll(str, "");
}
}
log.info("【XSS攻擊防禦】,特殊符號處理後是:{}", value);
}
}
return value;
}
}
測試類
package com.newcapec.cloudpay.controller.test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import javax.servlet.http.HttpServletRequest;
/**
* @Title: Controller--測試
* @ClassName:RsaControllerTest.java
* @Description:
*
* @Copyright 2016-2017 Powered By 研發中心
* @author: 王延飛
* @version V1.0
*/
@Controller
@RequestMapping("/test")
public class RsaControllerTest {
private static final Logger log = LoggerFactory.getLogger(RsaControllerTest.class);
@RequestMapping("/testXSS")
@ResponseBody
public String testXSS(HttpServletRequest request){
/*String queryString = request.getQueryString();
log.info("【防禦XSS】,客戶端端請求參數是:{}", queryString);*/
String name = request.getParameter("name");
log.info("【防禦XSS】,客戶端端name是:{}", name);
return "【防禦XSS】,客戶端端請求參數:" +name;
}
}
測試結果
訪問:http://localhost:8080/CloudPayment/test/testXSS.do?name=<script>alert('你被攻擊了!')</script>
- 控制檯輸出信息:
2019-01-25 11:50:37:512 com.newcapec.cloudpay.filter.XssFilterUtil.stripXss(XssFilterUtil.java:104) 【XSS攻擊防禦】,接收字符是:<script>alert('你被攻擊了!')</script>
2019-01-25 11:50:37:513 com.newcapec.cloudpay.filter.XssFilterUtil.stripXss(XssFilterUtil.java:118) 【XSS攻擊防禦】,匹配正則是:java.util.regex.Matcher[pattern=<+\s*\w*\s*(oncontrolselect|oncopy|oncut|ondataavailable|ondatasetchanged|ondatasetcomplete|ondblclick|ondeactivate|ondrag|ondragend|ondragenter|ondragleave|ondragover|ondragstart|ondrop|onerror=|onerroupdate|onfilterchange|onfinish|onfocus|onfocusin|onfocusout|onhelp|onkeydown|onkeypress|onkeyup|onlayoutcomplete|onload|onlosecapture|onmousedown|onmouseenter|onmouseleave|onmousemove|onmousout|onmouseover|onmouseup|onmousewheel|onmove|onmoveend|onmovestart|onabort|onactivate|onafterprint|onafterupdate|onbefore|onbeforeactivate|onbeforecopy|onbeforecut|onbeforedeactivate|onbeforeeditocus|onbeforepaste|onbeforeprint|onbeforeunload|onbeforeupdate|onblur|onbounce|oncellchange|onchange|onclick|oncontextmenu|onpaste|onpropertychange|onreadystatechange|onreset|onresize|onresizend|onresizestart|onrowenter|onrowexit|onrowsdelete|onrowsinserted|onscroll|onselect|onselectionchange|onselectstart|onstart|onstop|onsubmit|onunload)+\s*=+ region=0,0 lastmatch=],處理後是:
2019-01-25 11:50:37:513 com.newcapec.cloudpay.controller.test.RsaControllerTest.testXSS(RsaControllerTest.java:64) 【防禦XSS】,客戶端端name是: