新項(xiàng)目為了防止XSS攻擊辽装,直接把所有的html標(biāo)簽都過濾成""了,導(dǎo)致有個(gè)地方需要編輯存儲(chǔ)富文本的功能用不了了/(ㄒoㄒ)/~~透葛,產(chǎn)品讓我改续镇,我表示還沒寫過專門針對(duì)富文本的過濾器,我也沒好好研究過javaWeb的過濾器陵究,今天學(xué)習(xí)了一下眠饮。寫了個(gè)比較簡單的針對(duì)XSS攻擊的過濾器。
基本思路就是把http請(qǐng)求的參數(shù)攔截下來铜邮,針對(duì)一些特殊的字符過濾一遍仪召。
首先寫一個(gè)過濾器寨蹋。
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,創(chuàng)建裝飾類扔茅,以達(dá)到修改HttpServletRequest參數(shù)的目的
*/
private class ModifyParametersWrapper extends HttpServletRequestWrapper {
private Map<String, String[]> requestParams;
public ModifyParametersWrapper(HttpServletRequest request) {
super(request);
}
/**
* 獲取指定參數(shù)名的值已旧,如果有重復(fù)的參數(shù)名,則返回第一個(gè)的值 接收一般變量 召娜,如text類型
*
* @param name 指定參數(shù)名
* @return 指定參數(shù)名的值
*/
@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;
}
/**
* 獲取指定參數(shù)名的所有值的數(shù)組
*/
@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()) {
//對(duì)參數(shù)名進(jìn)行過濾
String key = HTMLFilterUtil.cleanXSS(entry.getKey());
//對(duì)每個(gè)傳參進(jìn)行過濾
String[] rawValues = entry.getValue();
String[] filteredValues = new String[rawValues.length];
for (int i = 0; i < rawValues.length; i++) {
//具體的過濾規(guī)則
filteredValues[i] = HTMLFilterUtil.cleanXSS((rawValues[i]));
}
requestParams.put(key, filteredValues);
}
}
}
return requestParams;
}
}
具體的過濾規(guī)則
/**
* 標(biāo)簽部分轉(zhuǎn)譯
* @param value
* @return
*/
public static String cleanXSS(String value) {
//屏蔽掉xss攻擊和sql注入等危險(xiǎn)字符
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;
}
/**
* 標(biāo)簽全過濾
* @param inputString
* @return
*/
public static String Html2Text(String inputString) {
String htmlStr = inputString; //含html標(biāo)簽的字符串
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的正則表達(dá)式{或<script[^>]*?>[\\s\\S]*?<\\/script> }
String regEx_style = "<[\\s]*?style[^>]*?>[\\s\\S]*?<[\\s]*?\\/[\\s]*?style[\\s]*?>"; //定義style的正則表達(dá)式{或<style[^>]*?>[\\s\\S]*?<\\/style> }
String regEx_html = "<[^>]+>"; //定義HTML標(biāo)簽的正則表達(dá)式
p_script = Pattern.compile(regEx_script, Pattern.CASE_INSENSITIVE);
m_script = p_script.matcher(htmlStr);
htmlStr = m_script.replaceAll(""); //過濾script標(biāo)簽
p_style = Pattern.compile(regEx_style, Pattern.CASE_INSENSITIVE);
m_style = p_style.matcher(htmlStr);
htmlStr = m_style.replaceAll(""); //過濾style標(biāo)簽
p_html = Pattern.compile(regEx_html, Pattern.CASE_INSENSITIVE);
m_html = p_html.matcher(htmlStr);
htmlStr = m_html.replaceAll(""); //過濾html標(biāo)簽
textStr = htmlStr;
// 過濾單雙引號(hào)
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的參數(shù)运褪,但是如果要過濾直接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];
}
//可以對(duì)字符串進(jìn)行操作玖瘸,但是我覺得json這種的還是反序列話為對(duì)象之后再處理比較好秸讹,
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()));
}
最后需要把過濾器配置好。
下面是配置多個(gè)過濾器的方法雅倒。
@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;
}
/**
* 創(chuàng)建一個(gè)bean
* @return
*/
@Bean(name = "xssFilter")
public Filter xssFilter() {
return new XSSFilter();
}
/**
* 創(chuàng)建一個(gè)bean
* @return
*/
@Bean(name = "sessionFilter")
public Filter sessionFilter() {
return new SessionFilter();
}
}
這樣一個(gè)基本的過濾器就完成了蔑匣。