发布于2020-11-19 20:46 阅读(1571) 评论(0) 点赞(9) 收藏(2)
项目中经常遇到sql注入和XSS攻击,此时我们需要编写拦截器来对前台上传参数进行合法性校验。下面为从请求中读取参数的一些规则:
首先我们需要一个过滤器在过滤器中对请求进行包装并替换到过滤链中,我们把这个过滤器配置为第一个过滤器,这样在后面的过滤器或拦截器中就可以放心的进行参数读取
RequestWrapperFilter。java
package until;
import org.springframework.web.multipart.MultipartHttpServletRequest;
import org.springframework.web.multipart.MultipartResolver;
import org.springframework.web.multipart.commons.CommonsMultipartResolver;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
/**
* @Description: 包装请求对象过滤器,方便拦截器可重复的读取包装对象克隆的参数
* 后面拦截器或过滤器 获取到的请求体类型为MultipartHttpServletRequest 或者RequestWrapper 可进行参数读取
*/
public class RequestWrapperFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
ServletRequest requestWrapper = null;
if(servletRequest instanceof HttpServletRequest) {
HttpServletRequest request = (HttpServletRequest) servletRequest;
if (request.getContentType() != null && request.getContentType().contains("multipart/form-data")) { // 文件类型
MultipartResolver resolver = new CommonsMultipartResolver(request.getSession().getServletContext());
MultipartHttpServletRequest multipartRequest = resolver.resolveMultipart(request);
request = multipartRequest;
filterChain.doFilter(request, servletResponse); // 将转化后的 request 放入过滤链中
}else{ // 非文件类型包装
requestWrapper = new RequestWrapper((HttpServletRequest) servletRequest);
filterChain.doFilter(requestWrapper, servletResponse);
}
}else{
filterChain.doFilter(servletRequest, servletResponse);
}
}
@Override
public void destroy() {
}
}
web.xml
<!-- 包装请求过滤器 -->
<filter>
<filter-name>requestWrapperFilter</filter-name>
<filter-class>until.RequestWrapperFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>requestWrapperFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
RequestWrapper.java
package until;
import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;
import javax.servlet.ReadListener;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.io.*;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import java.util.Vector;
/**
* @Description: 包装请求
* 1.拦截器使用
* .拦截器接受RequestWrapperFilter包装后的请求
*/
public class RequestWrapper extends HttpServletRequestWrapper {
/**
* 日志对象
*/
private Logger logger = Logger.getLogger(this.getClass());
private final String body;
private String code ="utf-8";
private Map<String, String[]> parameterMap = new HashMap<>(); // 所有参数的Map集合
public RequestWrapper(HttpServletRequest request) throws UnsupportedEncodingException {
super(request);
StringBuffer stringBuffer = new StringBuffer();
BufferedReader bufferedReader = null;
InputStream inputStream = null;
try {
inputStream = request.getInputStream();
if (inputStream != null) {
bufferedReader = new BufferedReader(new InputStreamReader(inputStream,code));
String readLine;
while ((readLine = bufferedReader.readLine()) != null) {
stringBuffer.append(readLine);
}
} else {
stringBuffer.append("");
}
} catch (IOException ex) {
logger.error("RequestWrapper: 包装请求"+ ExceptionUtil.getStackTraceString(ex));
} finally {
if (inputStream != null) {
try {
inputStream.close();
}catch (IOException e) {
logger.error("RequestWrapper: 包装请求 关闭流失败"+ ExceptionUtil.getStackTraceString(e));
}
}
if (bufferedReader != null) {
try {
bufferedReader.close();
}catch (IOException e) {
logger.error("RequestWrapper: 包装请求 关闭缓冲区失败"+ ExceptionUtil.getStackTraceString(e));
}
}
}
String body1 =stringBuffer.toString();
body = StringUtil.urlCodeSpeciCharToStr(body1,code);
if(StringUtils.isNotEmpty(body1)){
String []paramArr = body1.split("&");
if(paramArr != null && paramArr.length >0){
for(String param : paramArr){
if(StringUtils.isNotEmpty(param)){
String [] keyValueArr = param.split("=");
if(keyValueArr != null ){
if(keyValueArr.length == 2 && StringUtils.isNotEmpty(keyValueArr[0])){
String arr[]={ StringUtil.urlCodeSpeciCharToStr(keyValueArr[1],code)};
parameterMap.put( StringUtil.urlCodeSpeciCharToStr(keyValueArr[0],code),arr);
}else{
String arr[]={ ""};
parameterMap.put( StringUtil.urlCodeSpeciCharToStr(keyValueArr[0],code),arr); // 有键为""无键为null
}
}
}
}
}
}else{
parameterMap = request.getParameterMap(); // get请求 从流中获取不到参数,需要以此方式获取参数
}
}
@Override
public ServletInputStream getInputStream() throws IOException {
final ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(body.getBytes(code));
ServletInputStream servletInputStream = new ServletInputStream() {
@Override
public boolean isFinished() {
return false;
}
@Override
public boolean isReady() {
return false;
}
@Override
public void setReadListener(ReadListener readListener) {
}
@Override
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;
}
/**
* 获取所有参数名
* @return 返回所有参数名
*/
@Override
public Enumeration<String> getParameterNames() {
Vector<String> vector = new Vector<String>(parameterMap.keySet());
return vector.elements();
}
/**
* 获取指定参数名的值,如果有重复的参数名,则返回第一个的值 接收一般变量 ,如text类型
* @param name 指定参数名
* @return 指定参数名的值
*/
@Override
public String getParameter(String name) {
String[] results = parameterMap.get(name);
if(results != null && results.length>0){
return results[0];
}
return null;
}
/**
* 获取指定参数名的所有值的数组,如:checkbox的所有数据
* 接收数组变量 ,如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;
}
}
StringUtil.java
package until;
import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class StringUtil {
private StringUtil() {
}
/**
* 日志
*/
private static Logger logger = Logger.getLogger(StringUtil.class);
/**
* @Description: URLEncoder转为字符串
* URLDecoder的decode方法会把加号+和%20都解码为空格,
* URLEncoder.encode会把 加号+已被编码成%2B)
* @param str 字符串
* @param code 编码方式
* @return java.lang.String
* @throws
*/
public static String urlCodeSpeciCharToStr(String str,String code) throws UnsupportedEncodingException {
if(StringUtils.isNotEmpty(str)){
str = str.replaceAll("\\+", "%20"); //空格+转为编码
str = URLDecoder.decode(str,code);
HashMap<String,String> charMap = new HashMap<>();
charMap.put("\\%20"," ");
charMap.put("\\%2B","+");
charMap.put("\\%2F","/");
charMap.put("\\%3F","?");
charMap.put("\\%25","%");
charMap.put("\\%23","#");
charMap.put("\\%26","&");
charMap.put("\\%3D","=");
for (Map.Entry<String, String> entry : charMap.entrySet()) {
str = str.replaceAll(entry.getKey(),entry.getValue());
}
return str;
}else{
return str;
}
}
}
至此,我们的请求就包装完毕,下面就开始从我们包装后的请求中获取参数进行校验
XssInterceptor.java
package until;
import com.alibaba.fastjson.JSONObject;
import org.apache.log4j.Logger;
import org.springframework.web.multipart.MultipartHttpServletRequest;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Map;
import java.util.Set;
public class XssInterceptor implements HandlerInterceptor {
/**
* 日志对象
*/
private Logger logger = Logger.getLogger(this.getClass());
@Override
public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o) throws Exception {
if(httpServletRequest instanceof RequestWrapper || httpServletRequest instanceof MultipartHttpServletRequest){
XssCheckUtils checkUtils = new XssCheckUtils();
String pValue = null;
String type="0";//校验模式 0:严格模式;1:html模式;2sql模式
Map<String,String[]> pramMap = httpServletRequest.getParameterMap();
if(pramMap != null && ! pramMap.isEmpty()){
Set<String> keySet = pramMap.keySet();
for(String key : keySet){
pValue = (pramMap.get(key) != null && pramMap.get(key).length >0 ) ? pramMap.get(key)[0] : "";
pValue = pValue.toUpperCase();
if (checkUtils.safeProtectFilter(pValue,type)) {//有非法字符
JSONObject jsonObject = new JSONObject();
jsonObject.put("code","1");
jsonObject.put("message","参数"+pValue+"含有非法字符");
returnJson(httpServletResponse,jsonObject.toJSONString());
return false;
}
}
}
}
return true;
}
/**
* 返回客户端数据
*/
private void returnJson(HttpServletResponse response, String result) throws Exception {
PrintWriter writer = null;
response.reset();//重置response
response.setCharacterEncoding("UTF-8");
response.setContentType("text/html; charset=utf-8");
response.setStatus(707);//前台识别为参数非法
response.setContentType("application/json;charset=UTF-8");
try {
writer = response.getWriter();
writer.print(result);
} catch (IOException e) {
} finally {
if (writer != null) {
writer.close();
}
}
}
@Override
public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception {
}
@Override
public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception {
}
}
正则校验类XssCheckUtils.java
package until;
import java.util.regex.Pattern;
public class XssCheckUtils {
private static Pattern strictInvalidInputPattern = null;//严格校验
private static Pattern looseInvalidInputPattern = null;//宽松校验 h5 相关元素
static {
try {
strictInvalidInputPattern = Pattern.compile("<[\\s\\x00]*SCRIPT" + "|SELECT\\s" + "|ONFOCUS[\\s\\x00]*="
+ "|STYLE[\\s\\x00]*=" + "|ONDBLCLICK[\\s\\x00]*=" + "|ONERROR[\\s\\x00]*=" + "|INSERT\\s"
+ "|DELETE\\s" + "|ONKEYPRESS[\\s\\x00]*=" + "|LOCATION[\\s\\x00]*=" + "|UPDATE\\s" + "|DROP\\s"
+ "|<!--|-->" + "|ONCLICK[\\s\\x00]*=ONDBLCLICK[\\s\\x00]*=" + "|ONHELP[\\s\\x00]*="
+ "|ONKEYDOWN[\\s\\x00]*=" + "|ONKEYPRESS[\\s\\x00]*=" + "|ONKEYUP[\\s\\x00]*="
+ "|ONMOUSEDOWN[\\s\\x00]*=" + "|ONMOUSEMOVE[\\s\\x00]*=" + "|ONMOUSEOUT[\\s\\x00]*="
+ "|ONMOUSEOVER[\\s\\x00]*=" + "|ONMOUSEUP[\\s\\x00]*=" + "|ONABORT[\\s\\x00]*="
+ "|ONERROR[\\s\\x00]*=" + "|ONLOAD[\\s\\x00]*=" + "|ONBLUR[\\s\\x00]*=" + "|ONCHANGE[\\s\\x00]*="
+ "|ONCLICK[\\s\\x00]*=" + "|[\\s\\x00]*REFERER[\\s\\x00]*"
+ "|[\\s\\x00]+ID[\\s\\x00]*=" + "|CLASS[\\s\\x00]*=" + "|CALL[\\s\\x00]*=" + "|EVAL[\\s\\x00]*="
+ "|\\.[\\s\\x00]*LOAD[\\s\\x00]*\\(" + "|LOCATION[\\s\\x00]*\\.[\\s\\x00]*REPLACE[\\s\\x00]*\\("
+ "|NAVIGATE[\\s\\x00]*\\(" + "|ONRESIZE[\\s\\x00]*=" + "|ONUMLOAD[\\s\\x00]*="
+ "|DOCUMENT[\\s\\x00]*\\.\\[" + "|DOCUMENT[\\s\\x00]*\\.[\\s\\x00]*COOKIE"
+ "|HANDLEEVENT[\\s\\x00]*\\(" + "|ROUTEEVENT[\\s\\x00]*\\(" + "|CAPTUREEVENTS\\("
+ "|\\.[\\s\\x00]*WRITE[\\s\\x00]*\\(" + "|\\.[\\s\\x00]*WRITELN[\\s\\x00]*\\("
+ "|\\.[\\s\\x00]*ACTION[\\s\\x00]*=" + "|HISTORY[\\s\\x00]*\\.[\\s\\x00]*BACK[\\s\\x00]*\\("
+ "|HISTORY[\\s\\x00]*\\.[\\s\\x00]*GO[\\s\\x00]*\\(" + "|WINDOWS[\\s\\x00]*\\["
+ "|DOCUMENT[\\s\\x00]*\\["
+ "|STRING[\\s\\x00]*\\.[\\s\\x00]*FROMCHARCODE[\\s\\x00]*\\("
+ "|ALERT[\\s\\x00]*\\(|CONFIRM[\\s\\x00]*\\(|FORM[\\s\\x00]*=" + "|FORMACTION[\\s\\x00]*="
+ "|POSTER[\\s\\x00]*=" + "|ONINPUT[\\s\\x00]*=" + "|DIRNAME[\\s\\x00]*="
+ "|BACKGROUNG[\\s\\x00]*=" + "|[\\s\\x00\\x27]+OR[\\s\\x00\\x27]+"
+ "|[\\s\\x00\\x27]+LIKE[\\s\\x00\\x27]+" + "|[\\s\\x00\\x27]+\\|\\[\\s\\x00\\x27]+"
+ "|DECLARE[\\s\\x00]+[\\w\\W]*[\\s\\x00]+VARCHAR"
+ "|DECLARE[\\s\\x00]+[\\w\\W]*[\\s\\x00]+NVARCHAR"
+ "|DECLARE[\\s\\x00]+[\\w\\W]*[\\s\\x00]+CURSOR"
+ "|SET[\\s\\x00]+[\\w\\W]*[\\s\\x00]*=[\\s\\x00]*CAST"
+ "|PROMPT[\\s\\x00]*\\(\\\"[\\s\\x00]*>[\\s\\x00]*<" + "|<FRAME|<IFRAME|<FRAMESET|<NOFRAME"
+ "|<PLAINTEXT|<A|<LINK|<MAP|<BGSOUND" + "|<FORM|<INPUT|<SELECT|<OPTION|<TEXTAREA"
+ "|<IMG|<APPLET|<OBJECT|<EMBED|<NOSCRIPT|<STYLE" + "|ALERT[\\s\\x00]*\\(" + "|OPEN[\\s\\x00]*\\("
+ "|[\\s\\x00]*REFERER[\\s\\x00]*"
+ "|[\\s\\x00]*PROMPT[\\s\\x00]*" + "|%[\\d00]+" + "|SHOWMODALDIALOG[\\s\\x00]*\\("
+ "|SHOWMODELESSDIALOG[\\s\\x00]*\\(");
looseInvalidInputPattern = Pattern.compile("SELECT\\s"
+ "|INSERT\\s"
+ "|DELETE\\s" + "|UPDATE\\s" + "|DROP\\s"
+ "|HISTORY[\\s\\x00]*\\.[\\s\\x00]*GO[\\s\\x00]*\\(" + "|WINDOWS[\\s\\x00]*\\["
+ "|DOCUMENT[\\s\\x00]*\\["
+ "|STRING[\\s\\x00]*\\.[\\s\\x00]*FROMCHARCODE[\\s\\x00]*\\("
+ "|DECLARE[\\s\\x00]+[\\w\\W]*[\\s\\x00]+VARCHAR"
+ "|DECLARE[\\s\\x00]+[\\w\\W]*[\\s\\x00]+NVARCHAR"
+ "|DECLARE[\\s\\x00]+[\\w\\W]*[\\s\\x00]+CURSOR");
} catch (Exception err) {
strictInvalidInputPattern = null;
looseInvalidInputPattern = null;
}
}
public XssCheckUtils() {
}
/**
* 正则校验值
* @param parStr 值串
* @param type 0(默认):严格模式;1:宽松模式
* @return
*/
public boolean safeProtectFilter(String parStr,String type) {
if (parStr != null) {
parStr = parStr.toUpperCase();
if(type != null && "1".equals(type)){//宽松校验
if (looseInvalidInputPattern.matcher(parStr).find()) {
return true;
}
}else{//严格校验
if (strictInvalidInputPattern.matcher(parStr).find()) {
return true;
}
}
}
return false;
}
}
springmvc.xml
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/**"/>
<mvc:exclude-mapping path="/xx/xx" /> <!--白名单接口-->
<bean class="until.XssInterceptor"/>
</mvc:interceptor>
</mvc:interceptors>
原文链接:https://blog.csdn.net/weixin_38861291/article/details/109777166
作者:java之恋
链接:http://www.javaheidong.com/blog/article/879/32c15fcbba1d653779e6/
来源:java黑洞网
任何形式的转载都请注明出处,如有侵权 一经发现 必将追究其法律责任
昵称:
评论内容:(最多支持255个字符)
---无人问津也好,技不如人也罢,你都要试着安静下来,去做自己该做的事,而不是让内心的烦躁、焦虑,坏掉你本来就不多的热情和定力
Copyright © 2018-2021 java黑洞网 All Rights Reserved 版权所有,并保留所有权利。京ICP备18063182号-2
投诉与举报,广告合作请联系vgs_info@163.com或QQ3083709327
免责声明:网站文章均由用户上传,仅供读者学习交流使用,禁止用做商业用途。若文章涉及色情,反动,侵权等违法信息,请向我们举报,一经核实我们会立即删除!