今天天氣晴朗出刷,氣候炎熱,是時候來一點java代碼來降降溫了坯辩。
在做web開發(fā)的時候馁龟,可能會遇到無聊的人,或者對你的網(wǎng)站圖謀不軌的人漆魔,當(dāng)然這個是假如坷檩。俗話說:害人之心不可有,防人之心不可無改抡。那么矢炼,我們就來做一個XSS過濾器,來抵擋一部分惡意的訪問吧阿纤。
講一下原理:把request中的請求參數(shù)放入我們自己寫的包裝request類中句灌,然后寫一個過濾器對這個包裝request中的參數(shù)進行過濾。
為什么需要對Request進行一次包裝呢欠拾?直接用不是更好嗎胰锌。其實這里面主要是針對post帶請求體這種的,如果要拿請求體中的參數(shù)藐窄,就要從request獲取去參數(shù)资昧,也就是從流中獲取參數(shù)。但是一旦從流中獲取到參數(shù)枷邪,在后面流也就關(guān)閉了榛搔,無法再次獲取,再次獲取就會報錯东揣。就好比男生在飛機完就會進入賢者模式践惑,此刻你再來一個刺激,那就事情不妙了嘶卧。在代碼中也是一樣的尔觉,也就是我們的Controler無法獲取到參數(shù)。所以芥吟,才有了包裝request的事侦铜。
好,開啟代碼之旅吧钟鸵。
首先钉稍,就是定義一些常量,后面的代碼會用得著:
package cn.wjp.mydaily.common.filter;
public class HttpConst {
/**
* 幾種常見的Content-Type
*/
public static final String FORM_URLENCODED_CONTENT_TYPE ="application/x-www-form-urlencoded";
public static final String JSON_CONTENT_TYPE = "application/json";
public static final String MULTIPART_CONTENT_TYPE = "multipart/form-data";
/**
* 常見的post/get請求方式
*/
public static final String POST_METHOD = "post";
public static final String GET_METHOD = "get";
public static final String OPTIONS_METHOD = "options";
}
接下來需要分別針對不同的表單提交類型寫包裝類棺耍,對于普通的get/post請求的包裝類:
package cn.wjp.mydaily.common.filter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.io.IOException;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import java.util.Vector;
/**
* 適用于普通get post請求 不包含multipart/form-data application/json等請求
* 緩存請求參數(shù) 重寫獲取參數(shù)的方法
*/
public class HttpServletRequestNormalWrapper extends HttpServletRequestWrapper {
private Map<String, String[]> parameterMap = new HashMap<>(); // 所有參數(shù)的Map集合
public HttpServletRequestNormalWrapper(HttpServletRequest request){
super(request);
Enumeration params = request.getParameterNames();//獲得所有請求參數(shù)名
StringBuffer paramsValue = new StringBuffer("");
while (params.hasMoreElements()) {
String name = params.nextElement().toString(); //得到參數(shù)名
String[] value = request.getParameterValues(name);//得到參數(shù)對應(yīng)值
parameterMap.put(name,value);
}
}
/**
* 獲取所有參數(shù)名
*
* @return 返回所有參數(shù)名
*/
@Override
public Enumeration<String> getParameterNames() {
Vector<String> vector = new Vector<String>(parameterMap.keySet());
return vector.elements();
}
/**
* 獲取指定參數(shù)名的值贡未,如果有重復(fù)的參數(shù)名,則返回第一個的值 接收一般變量 ,如text類型
*
* @param name 指定參數(shù)名
* @return 指定參數(shù)名的值
*/
@Override
public String getParameter(String name) {
String[] values = parameterMap.get(name);
if(values==null||values.length==0){
return null;
}
return values[0];
//如果有多個參數(shù)值的俊卤,請放開該注釋 我這里沒有做這么細致
/*StringBuffer sb = new StringBuffer();
for(int i=0;i<values.length;i++){
if(i==values.length-1){
sb.append(values[i]);
}else{
sb.append(values[i]).append(",");
}
}
return sb.toString();*/
}
/**
* 獲取指定參數(shù)名的所有值的數(shù)組嫩挤,如:checkbox的所有數(shù)據(jù)
* 接收數(shù)組變量 ,如checkobx類型
*/
@Override
public String[] getParameterValues(String name) {
return parameterMap.get(name);
}
@Override
public Map<String, String[]> getParameterMap() {
return parameterMap;
}
public void setParameterMap(Map<String, String[]> parameterMap) {
this.parameterMap = parameterMap;
}
}
對于post+application/json的請求消恍,對應(yīng)的包裝類:
package cn.wjp.mydaily.common.filter;
import javax.servlet.ReadListener;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.io.*;
/**
* 將body中的數(shù)據(jù)緩存起來 重寫getInputStream getReader 等方法 適用于application/json的post請求
*/
public class HttpServletRequestBodyReaderWrapper extends HttpServletRequestWrapper{
private String body ="{}";//緩存請求體的內(nèi)容
public HttpServletRequestBodyReaderWrapper(HttpServletRequest request) throws IOException {
super(request);
StringBuilder stringBuilder = new StringBuilder("");
BufferedReader bufferedReader = null;
try {
InputStream inputStream = request.getInputStream();
if (inputStream != null) {
bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
char[] charBuffer = new char[1024];
int bytesRead = -1;
while ((bytesRead = bufferedReader.read(charBuffer)) > 0) {
stringBuilder.append(charBuffer, 0, bytesRead);
}
}
} catch (IOException ex) {
throw ex;
} finally {
if (bufferedReader != null) {
try {
bufferedReader.close();
} catch (IOException ex) {
throw ex;
}
}
}
body = stringBuilder.toString();
}
@Override
public ServletInputStream getInputStream() throws IOException {
final ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(body.getBytes("utf-8"));
ServletInputStream servletInputStream = new ServletInputStream() {
public boolean isFinished() {
return false;
}
public boolean isReady() {
return true;
}
public void setReadListener(ReadListener readListener) {}
public int read() throws IOException {
return byteArrayInputStream.read();
}
};
return servletInputStream;
}
@Override
public BufferedReader getReader() throws IOException {
return new BufferedReader(new InputStreamReader(this.getInputStream()));
}
public String getBody() {
return this.body;
}
public void setBody(String body) {
this.body = body;
}
}
接下來就是XSS校驗啦:
package cn.wjp.mydaily.common.filter;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import javax.servlet.*;
import javax.servlet.FilterConfig;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
public class XssFilter implements Filter {
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
HttpServletRequest request =(HttpServletRequest)req;
HttpServletResponse response =(HttpServletResponse)res;
String contentType = request.getContentType();//獲取contentType請求頭
String method = request.getMethod();//獲取請求方法 post/get
//1 處理get請求 get請求的Content-Type一般為application/x-www-form-urlencoded 或者 text/html
if(method.trim().equalsIgnoreCase(HttpConst.GET_METHOD)){
HttpServletRequestNormalWrapper wrapperRequest = new HttpServletRequestNormalWrapper(request);
Map<String, String[]> parameterMap = wrapperRequest.getParameterMap();
parameterMap =cleanXSSForNormalRequest(parameterMap);
wrapperRequest.setParameterMap(parameterMap);
chain.doFilter(wrapperRequest, response);
return;
}
//2 處理post請求 只處理application/x-www-form-urlencoded application/json,對于multipart/form-data岂昭,直接放行
if(method.trim().equalsIgnoreCase(HttpConst.POST_METHOD)){
if(contentType.trim().toLowerCase().contains(HttpConst.MULTIPART_CONTENT_TYPE)){
chain.doFilter(request, response);
return;
}
//處理application/x-www-form-urlencoded
if(contentType.trim().toLowerCase().contains(HttpConst.FORM_URLENCODED_CONTENT_TYPE)){
HttpServletRequestNormalWrapper wrapperRequest = new HttpServletRequestNormalWrapper(request);
Map<String, String[]> parameterMap = wrapperRequest.getParameterMap();
parameterMap =cleanXSSForNormalRequest(parameterMap);
wrapperRequest.setParameterMap(parameterMap);
chain.doFilter(wrapperRequest, response);
return;
}
//處理application/json
if(contentType.trim().toLowerCase().contains(HttpConst.JSON_CONTENT_TYPE)){
HttpServletRequestBodyReaderWrapper requestWrapper = new HttpServletRequestBodyReaderWrapper(request);
String body = requestWrapper.getBody();
body =cleanXSSForPostJsonRequest(body);
requestWrapper.setBody(body);
chain.doFilter(requestWrapper, response);
return;
}
}
chain.doFilter(request, response);
return;
}
public String cleanXSS(String value) {
if(value==null||value.trim().isEmpty()){
return null;
}
value = value.replaceAll("<", "& lt;").replaceAll(">", "& gt;");
value = value.replaceAll("\\(", "& #40;").replaceAll("\\)", "& #41;");
value = value.replaceAll("'", "& #39;");
value = value.replaceAll("\"", "& #34;");
value = value.replaceAll("`", "");
value = value.replaceAll("eval\\((.*)\\)", "");
value = value.replaceAll("[\\\"\\\'][\\s]*javascript:(.*)[\\\"\\\']", "\"\"");
value = value.replaceAll("script", "");
return value;
}
/**
* 普通的post/get請求
* @param parameterMap
*/
public Map<String,String[]> cleanXSSForNormalRequest(Map<String,String[]> parameterMap){
Map<String,String[]> cleanMap = new HashMap<>();
if(parameterMap==null||parameterMap.size()==0){
return cleanMap;
}
for (Map.Entry<String,String[]> entry : parameterMap.entrySet()) {
String key = entry.getKey();
String[] value = entry.getValue();
String cleanKey = cleanXSS(key);
String[] cleanValue = null;
if(value!=null&&value.length>0){
cleanValue = new String[value.length];
for(int i=0;i<value.length;i++){
cleanValue[i]=cleanXSS(value[i]);
}
}
cleanMap.put(cleanKey,cleanValue);
}
//打印用
StringBuffer printStr = new StringBuffer();
for (Map.Entry<String,String[]> entry1 : cleanMap.entrySet()) {
printStr.append(entry1.getKey()).append("=").append(Arrays.asList(entry1.getValue())).append("&");
}
System.out.println("XssFilter:發(fā)送的請求參數(shù):"+JSON.toJSONString(printStr));
return cleanMap;
}
/**
* post的application/json請求
* @param body
*/
public String cleanXSSForPostJsonRequest(String body){
String cleanBody = "{}";
if(body==null||body.trim().isEmpty()||body.trim().equalsIgnoreCase("{}")||!body.trim().contains(":")){
return cleanBody;
}
Map<String,Object> map = JSON.parseObject(body,new TypeReference<Map<String,Object>>(){});
if(map==null||map.size()==0){
return cleanBody;
}
Map<String,Object> cleanMap = new HashMap<>();
for (Map.Entry<String,Object> entry : map.entrySet()) {
String key = entry.getKey();
Object value = entry.getValue();
String valueStr = String.valueOf(value);
if(valueStr==null||valueStr.trim().isEmpty()||valueStr.trim().equalsIgnoreCase("null")){
valueStr = null;
}
cleanMap.put(cleanXSS(key),cleanXSS(valueStr));
}
cleanBody = JSON.toJSONString(cleanMap);
System.out.println("XssFilter:發(fā)送的請求參數(shù):"+cleanBody);
return cleanBody;
}
@Override
public void destroy() {
}
@Override
public void init(FilterConfig arg0) {
}
}
“好,那個誰狠怨,到我辦公室來一下约啊。今天的xss過濾工作做得不錯,就獎勵你今晚一個加班吧取董,要知道棍苹,這個加班名額真的好不容易,我費了很大的勁才說服咱們組今晚早點下班的茵汰,好讓你有加班的機會枢里,你可以不要讓我失望喲。好蹂午,你繼續(xù)忙吧”栏豺。