新项目为了防止XSS攻击,直接把所有的html标签都过滤成""了,导致有个地方需要编辑存储富文本的功能用不了了/(ㄒoㄒ)/~~,产品让我改,我表示还没写过专门针对富文本的过滤器,我也没好好研究过javaWeb的过滤器,今天学习了一下。写了个比较简单的针对XSS攻击的过滤器。
基本思路就是把http请求的参数拦截下来,针对一些特殊的字符过滤一遍。
首先写一个过滤器。
public class XSSFilter extends OncePerRequestFilter {
@Override
protected void doFilterInternal(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, FilterChain filterChain) throws ServletException, IOException {
ModifyParametersWrapper wrapper = new ModifyParametersWrapper((HttpServletRequest) httpServletRequest);
filterChain.doFilter(wrapper, httpServletResponse);
}
/**
* 继承HttpServletRequestWrapper,创建装饰类,以达到修改HttpServletRequest参数的目的
*/
private class ModifyParametersWrapper extends HttpServletRequestWrapper {
private Map<String, String[]> requestParams;
public ModifyParametersWrapper(HttpServletRequest request) {
super(request);
}
/**
* 获取指定参数名的值,如果有重复的参数名,则返回第一个的值 接收一般变量 ,如text类型
*
* @param name 指定参数名
* @return 指定参数名的值
*/
@Override
public String getParameter(String name) {
String parameter = null;
String[] vals = getParameterMap().get(name);
if (vals != null && vals.length > 0) {
parameter = vals[0];
}
return parameter;
}
/**
* 获取指定参数名的所有值的数组
*/
@Override
public String[] getParameterValues(String name) {
return getParameterMap().get(name);
}
@Override
public Map<String, String[]> getParameterMap() {
if (requestParams == null) {
requestParams = new HashMap<String, String[]>();
Map<String, String[]> originalQueryString = super.getParameterMap();
if (originalQueryString != null) {
for (Map.Entry<String, String[]> entry : originalQueryString.entrySet()) {
//对参数名进行过滤
String key = HTMLFilterUtil.cleanXSS(entry.getKey());
//对每个传参进行过滤
String[] rawValues = entry.getValue();
String[] filteredValues = new String[rawValues.length];
for (int i = 0; i < rawValues.length; i++) {
//具体的过滤规则
filteredValues[i] = HTMLFilterUtil.cleanXSS((rawValues[i]));
}
requestParams.put(key, filteredValues);
}
}
}
return requestParams;
}
}
具体的过滤规则
/**
* 标签部分转译
* @param value
* @return
*/
public static String cleanXSS(String value) {
//屏蔽掉xss攻击和sql注入等危险字符
value = value.replaceAll("<", "<").replaceAll(">", ">");
value = value.replaceAll("\\(", "(").replaceAll("\\)", ")");
value = value.replaceAll("'", "'");
value = value.replaceAll("\"", """);
value = value.replaceAll("\\\\", "");
value = value.replaceAll("\\\\/", "");
value = value.replaceAll("eval\\((.*)\\)", "");
value = value.replaceAll("e-xpression\\\\((.*?)\\\\)\"", "");
value = value.replaceAll("[\\\"\\\'][\\s]*javascript:(.*)[\\\"\\\']", "\"\"");
value = value.replaceAll("[\\\"\\\'][\\s]*vbscript:(.*)[\\\"\\\']", "\"\"");
value = value.replaceAll("[\\\"\\\'][\\s]*onload:(.*)[\\\"\\\']", "\"\"");
return value;
}
/**
* 标签全过滤
* @param inputString
* @return
*/
public static String Html2Text(String inputString) {
String htmlStr = inputString; //含html标签的字符串
String textStr = "";
java.util.regex.Pattern p_script;
java.util.regex.Matcher m_script;
java.util.regex.Pattern p_style;
java.util.regex.Matcher m_style;
java.util.regex.Pattern p_html;
java.util.regex.Matcher m_html;
try {
String regEx_script = "<[\\s]*?script[^>]*?>[\\s\\S]*?<[\\s]*?\\/[\\s]*?script[\\s]*?>"; //定义script的正则表达式{或<script[^>]*?>[\\s\\S]*?<\\/script> }
String regEx_style = "<[\\s]*?style[^>]*?>[\\s\\S]*?<[\\s]*?\\/[\\s]*?style[\\s]*?>"; //定义style的正则表达式{或<style[^>]*?>[\\s\\S]*?<\\/style> }
String regEx_html = "<[^>]+>"; //定义HTML标签的正则表达式
p_script = Pattern.compile(regEx_script, Pattern.CASE_INSENSITIVE);
m_script = p_script.matcher(htmlStr);
htmlStr = m_script.replaceAll(""); //过滤script标签
p_style = Pattern.compile(regEx_style, Pattern.CASE_INSENSITIVE);
m_style = p_style.matcher(htmlStr);
htmlStr = m_style.replaceAll(""); //过滤style标签
p_html = Pattern.compile(regEx_html, Pattern.CASE_INSENSITIVE);
m_html = p_html.matcher(htmlStr);
htmlStr = m_html.replaceAll(""); //过滤html标签
textStr = htmlStr;
// 过滤单双引号
textStr = textStr.replaceAll("\'", "'");
textStr = textStr.replaceAll("\"", """);
textStr = textStr.replaceAll("\\(", "(").replaceAll("\\)", ")");
textStr = textStr.replaceAll("eval\\((.*)\\)", "");
textStr = textStr.replaceAll("\\\\", "");
textStr = textStr.replaceAll("\\\\/", "");
} catch (Exception e) {
System.err.println("Html2Text: " + e.getMessage());
}
return textStr;
}
这样可以过滤掉@RequestParam的参数,但是如果要过滤直接post的json字符串需要重写以下方法。
private byte[] requestBody = null;
public ModifyParametersWrapper(HttpServletRequest request) {
super(request);
try {
requestBody = StreamUtils.copyToByteArray(request.getInputStream());
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public ServletInputStream getInputStream() throws IOException {
if (requestBody == null) {
requestBody = new byte[0];
}
//可以对字符串进行操作,但是我觉得json这种的还是反序列话为对象之后再处理比较好,
String json = new String(requestBody, "UTF-8");
final ByteArrayInputStream bais = new ByteArrayInputStream(requestBody);
return new ServletInputStream() {
@Override
public int read() throws IOException {
return bais.read();
}
@Override
public boolean isFinished() {
return false;
}
@Override
public boolean isReady() {
return true;
}
@Override
public void setReadListener(ReadListener listener) {
}
};
}
@Override
public BufferedReader getReader() throws IOException {
return new BufferedReader(new InputStreamReader(getInputStream()));
}
最后需要把过滤器配置好。
下面是配置多个过滤器的方法。
@Configuration
public class FilterConfig {
/**
* 配置过滤器
* 按照order值的大小,从小到大的顺序来依次过滤
* @return
*/
@Bean
@Order(Integer.MAX_VALUE - 1)
public FilterRegistrationBean someFilterRegistration1() {
FilterRegistrationBean registration = new FilterRegistrationBean();
registration.setFilter(xssFilter());
registration.addUrlPatterns("/filter/*");
registration.addInitParameter("paramName", "paramValue");
registration.setName("xssFilter");
return registration;
}
/**
* 配置过滤器
* 按照order值的大小,从小到大的顺序来依次过滤
* @return
*/
@Bean
@Order(Integer.MAX_VALUE)
public FilterRegistrationBean someFilterRegistration2() {
FilterRegistrationBean registration = new FilterRegistrationBean();
registration.setFilter(sessionFilter());
registration.addUrlPatterns("/session/*");
registration.addInitParameter("paramName", "paramValue");
registration.setName("sessionFilter");
return registration;
}
/**
* 创建一个bean
* @return
*/
@Bean(name = "xssFilter")
public Filter xssFilter() {
return new XSSFilter();
}
/**
* 创建一个bean
* @return
*/
@Bean(name = "sessionFilter")
public Filter sessionFilter() {
return new SessionFilter();
}
}
这样一个基本的过滤器就完成了。