目录
前言
一、XSS危害
二、XSS防御
三、项目实战
1.过滤标签工具类编写
2.网关gateway出,过滤器拦截
1) 实现步骤
2)代码实现
总结
跨站脚本攻击(全称Cross Site Scripting,简称为XSS)是指恶意攻击者在Web页面中插入恶意javascript代码(也可能包含html代码),当用户浏览网页之时,嵌入其中Web里面的javascript代码会被执行,从而达到恶意攻击用户的目的
1.用户的Cookie被获取,其中可能存在Session ID等敏感信息。若服务器端没有做相应防护,攻击者可用对应Cookie登陆服务器。
2.攻击者能够在一定限度内记录用户的键盘输入。
3.攻击者通过CSRF等方式以用户身份执行危险操作。
4.XSS蠕虫。
5.获取用户浏览器信息。
6.利用XSS漏洞扫描用户内网。
1.标签过滤
2.事件过滤
3.敏感字符过滤
4.设置httponly防止Cookie被获取
5.内容安全策略(CSP)
6.在将不可信数据插入到HTML标签之间时,对这些数据进行HTML Entity编码
7.在将不可信数据插入到HTML属性里时,对这些数据进行HTML属性编码
8.在将不可信数据插入到SCRIPT里时,对这些数据进行SCRIPT编码
9.在将不可信数据插入到Style属性里时,对这些数据进行CSS编码
import org.apache.commons.lang3.StringUtils;
import org.owasp.validator.html.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpHeaders;
import org.springframework.util.CollectionUtils;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.util.MultiValueMapAdapter;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Pattern;
/**
* xss工具类
* @author liangxi.zeng
*/
public class XssUtils {
private static final Logger log = LoggerFactory.getLogger(XssUtils.class);
private static final String ANTISAMY_SLASHDOT_XML = "antisamy-slashdot-1.4.4.xml";
private static Policy policy = null;
private static final Pattern SCRIPT_BETWEEN_PATTERN = Pattern.compile("<[\r\n| | ]*script[\r\n| | ]*>(.*?)", Pattern.CASE_INSENSITIVE);
private static final Pattern SCRIPT_END_PATTERN = Pattern.compile("", Pattern.CASE_INSENSITIVE);
private static final Pattern SCRIPT_START_PATTERN = Pattern.compile("<[\r\n| | ]*script(.*?)>", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);
private static final Pattern EVAL_PATTERN = Pattern.compile("eval\\((.*?)\\)", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);
private static final Pattern E_XPRESSION_PATTERN = Pattern.compile("e-xpression\\((.*?)\\)", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);
private static final Pattern MOCHA_PATTERN = Pattern.compile("mocha[\r\n| | ]*:[\r\n| | ]*", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);
private static final Pattern EXPRESSION_PATTERN = Pattern.compile("expression\\((.*?)\\)", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);
private static final Pattern URL_PATTERN = Pattern.compile("url\\((.*?)\\)", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);
private static final Pattern VBSCRIPT_PATTERN = Pattern.compile("vbscript[\r\n| | ]*:[\r\n| | ]*", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);
private static final Pattern JAVASCRIPT_PATTERN = Pattern.compile("javascript[\r\n| | ]*:[\r\n| | ]*", Pattern.CASE_INSENSITIVE);
private static final Pattern ONLOAD_PATTERN = Pattern.compile("onload(.*?)=", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);
private static final Pattern ONMOUSEOVER_PATTERN = Pattern.compile("onMouseOver=.*?//", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);
private static final Pattern ONMOUSEOVER_PATTERN_2 = Pattern.compile("onmouseover(.*)", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);
private static final Pattern ONMOUSEOVER_PATTERN_3 = Pattern.compile("onmouseover=.*?", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);
private static final Pattern ALERT_PATTERN = Pattern.compile("alert(.*)", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);
private static String REPLACE_STRING = "";
private static Pattern script = null;
static {
script = Pattern.compile("<[\r\n| | ]*script[\r\n| | ]*>(.*?)", Pattern.CASE_INSENSITIVE);
log.debug(" start read XSS config file [" + ANTISAMY_SLASHDOT_XML + "]");
InputStream inputStream = XssUtils.class.getClassLoader().getResourceAsStream(ANTISAMY_SLASHDOT_XML);
try {
policy = Policy.getInstance(inputStream);
log.debug("read XSS config file [" + ANTISAMY_SLASHDOT_XML + "] success");
} catch (PolicyException e) {
log.error("read XSS config file [" + ANTISAMY_SLASHDOT_XML + "] fail , reason:", e);
} finally {
if (inputStream != null) {
try {
inputStream.close();
} catch (IOException e) {
log.error("close XSS config file [" + ANTISAMY_SLASHDOT_XML + "] fail , reason:", e);
}
}
}
}
/**
* 跨站攻击语句过滤 方法
*
* @param paramValue 待过滤的参数
* @param ignoreParamValueList 忽略过滤的参数列表
* @return 清理后的字符串
*/
public static String xssClean(String paramValue, List ignoreParamValueList) {
AntiSamy antiSamy = new AntiSamy();
try {
log.debug("raw value before xssClean: " + paramValue);
if (isIgnoreParamValue(paramValue, ignoreParamValueList)) {
log.debug("ignore the xssClean,keep the raw paramValue: " + paramValue);
return paramValue;
} else {
final CleanResults cr = antiSamy.scan(paramValue, policy);
cr.getErrorMessages().forEach(log::debug);
String str = cr.getCleanHTML();
str = stripXSSAndSql(str);
str = str.replaceAll(""", "\"");
str = str.replaceAll("&", "&");
// str = str.replaceAll("'", "'");
// str = str.replaceAll("'", "'");
// str = str.replaceAll(" ", "*");
str = str.replaceAll("<", "<");
str = str.replaceAll(">", ">");
log.debug("xss filter value after xssClean" + str);
return str;
}
} catch (ScanException e) {
log.error("scan failed is [" + paramValue + "]", e);
} catch (PolicyException e) {
log.error("antisamy convert failed is [" + paramValue + "]", e);
}
return paramValue;
}
/**
* 过滤形参
*
* @param paramValue
* @param ignoreParamValueList
* @param param
* @return
*/
public static String xssClean(String param,String paramValue, List ignoreParamValueList) {
if (isIgnoreParamValue(param, ignoreParamValueList)) {
//虽然过滤固定字段 允许标签 但是关键函数必须处理 不允许出现
return stripXSSAndSql(paramValue);
} else {
return xssClean(paramValue, ignoreParamValueList);
}
}
/**
* 过滤形参
*
* @param httpHeaders
* @param ignoreParamValueList
* @return
*/
public static void xssClean(HttpHeaders httpHeaders, List ignoreParamValueList) {
if(!CollectionUtils.isEmpty(httpHeaders)) {
for (Map.Entry> entry : httpHeaders.entrySet()) {
List paramValueList = entry.getValue();
String param = entry.getKey();
if (!CollectionUtils.isEmpty(paramValueList)) {
List xssCleanQueryParamList = new ArrayList<>();
paramValueList.forEach(paramValue -> {
String xssCleanValue = xssClean(param, paramValue, ignoreParamValueList);
xssCleanQueryParamList.add(xssCleanValue);
});
httpHeaders.put(param, xssCleanQueryParamList);
}
}
}
}
/**
* 过滤形参
*
* @param queryParamMap
* @param ignoreParamValueList
* @return
*/
public static MultiValueMap xssClean(MultiValueMap queryParamMap, List ignoreParamValueList) {
if(!CollectionUtils.isEmpty(queryParamMap)) {
MultiValueMap newQueryParamMap = new MultiValueMapAdapter<>(new HashMap<>());
for (Map.Entry> entry : queryParamMap.entrySet()) {
List paramValueList = entry.getValue();
String param = entry.getKey();
if (!CollectionUtils.isEmpty(paramValueList)) {
List xssCleanQueryParamList = new ArrayList<>();
paramValueList.forEach(paramValue -> {
String xssCleanValue = xssClean(param, paramValue, ignoreParamValueList);
xssCleanQueryParamList.add(xssCleanValue);
});
newQueryParamMap.put(param, xssCleanQueryParamList);
} else {
newQueryParamMap.put(param,paramValueList);
}
}
return newQueryParamMap;
}
return queryParamMap;
}
/**
* xss校验
*
* @param value
* @return
* @author 杨慕义
*/
public static String stripXSSAndSql(String value) {
if (StringUtils.isBlank(value)) {
return value;
}
// Avoid anything between script tags
value = SCRIPT_BETWEEN_PATTERN.matcher(value).replaceAll(REPLACE_STRING);
// Remove any lonesome tag
value = SCRIPT_END_PATTERN.matcher(value).replaceAll(REPLACE_STRING);
// Remove any lonesome