Java使用过滤器防止SQL注入XSS脚本注入的实现

网友投稿 267 2022-11-07


Java使用过滤器防止SQL注入XSS脚本注入的实现

前几天有个客户在系统上写了一段html语句,打开页面就显示一张炒鸡大的图片,影响美观。后来仔细想想,幸亏注入的仅仅是html语句,知道严重性后,马上开始一番系统安全配置。

一. 定义过滤器

package com.cn.unit.filter;

import java.io.IOException;

import javax.servlet.Filter;

import javax.servlet.FilterChain;

import javax.servlet.FilterConfig;

import javax.servlet.ServletException;

import javax.servlet.ServletRequest;

import javax.servlet.ServletResponse;

import javax.servlet.http.HttpServletRequest;

import org.apache.commons.lang.StringUtils;

import org.springframework.web.multipart.MultipartHttpServletRequest;

import org.springframework.web.multipart.commons.CommonsMultipartResolver;

/**

* 过滤器

* Created by adonis on 2020/12/12

*/

public class SafeFilter implements Filter{

// 配置信息对象

public FilterConfig filterConfig;

/**

* 初始化

* 与我们编写的Servlet程序一样,Filter的创建和销毁由WEB服务器负责。

* Web应用程序启动时,Web服务器将创建Filter的实例对象,并调用其init方法,读取web.xml配置,

* 完成对象的初始化功能,从而为后续的用户请求作好拦截的准备工作。

* Filter对象只会创建一次,init方法也只会执行一次。

* 开发人员通过init方法的参数,可获得代表当前Filter配置信息的FilterConfig对象。

*/

@Override

public void init(FilterConfig filterConfig) throws ServletException {

filterConfig = config;

}

/**

* 拦截请求

* 这个方法完成实际的过滤操作。当客户请求访问与过滤器关联的URL的时候,Servlet过滤器将先执行doFilter方法。

* FilterChain参数用于访问后续过滤器。

*/

@Override

public void doFilter(ServletRequest request, ServletResponse response,

FilterChain chain) throws IOException, ServletException {

HttpServletRequest httpRequest = (HttpServletRequest) request;

String enctype = httpRequest.getContentType();

if(StringUtils.isNotBlank(enctype) && enctype.contains("multipart/form-data")){

// 上传文件

CommonsMultipartResolver commonsMultipartResolver = new CommonsMultipartResolver(

httpRequest.getSession().getServletContext());

MultipartHttpServletRequest multipartRequest = commonsMultipartResolver.resolveMultipart(httpRequest);

XssHttpServletRequestWrapper xssRequest = new XssHttpServletRequestWrapper(multipartRequest);

chain.doFilter(xssRequest, response);

}else{

// 普通表单和Ajax

XssHttpServletRequestWrapper xssRequest = new XssHttpServletRequestWrapper((HttpServletRequest) request);

chain.doFilter(xssRequest, response);

}

}

/**

* 销毁

* Filter对象创建后会驻留在内存,当Web应用移除或服务器停止时才销毁。在Web容器卸载Filter对象之前被调用。

* 该方法在Filter的生命周期中仅执行一次。在这个方法中,可以释放过滤器使用的资源。

*/

@Override

public void destroy() {

this.filterConfig = null;

}

}

二. 过滤包装器,实现参数值过滤

package com.cn.unit.filter;

import java.util.Map;

import javax.servlet.http.HttpServletRequest;

import javax.servlet.http.HttpServletRequestWrapper;

/**

* 用户请求包装类

* Created by adonis on 2020/12/12

*/

public class SafeHttpServletRequestWrapper extends HttpServletRequestWrapper{

public SafeHttpServletRequestWrapper(HttpServletRequest request) {

super(request);

}

@Override

public String getParameter(String name) {

String value = super.getParameter(name);

if (value != null) {

value = xssEncode(value);

}

return value;

}

@Override

public String[] getParameterValues(String name) {

String[] value = super.getParameterValues(name);

if(value != null){

for (int i = 0; i < value.length; i++) {

value[i] = xssEncode(value[i]);

}

}

return value;

}

@Override

public Map getParameterMap() {

return super.getParameterMap();

}

/**

* 请求头不过滤

*/

@Override

public String getHeader(String name) {

return super.getHeader(name);

}

/**

* 将容易引起注入的关键字的半角字符直接替换成全角字符

* @param value 过滤前的值

* @return 过滤后的值

*/

private static String xssEncode(String value) {

if (value == null || value.isEmpty()) {

return value;

}

// 防SQL注入转义

value = StringEscapeUtils.escapeSql(value);

// HTML防注入,个人建议使用第三种

// 1.防HTML注入转义(HtmlUtils工具类,汉字不转义,双引号转义,存在jsON封装需要反转义)

value = HtmlUtils.htmlEscape(value);

/*

// 2.防HTML注入转义(StringEscapeUtils工具类,汉字也转义,取出时需要反转义)

// value = StringEscapeUtils.escapeHtml(value);

// 3.字符串替换法(通过各种循环替换字符串测试,最终还是replace替换效果最佳)

value = value.replaceAll("<", "<");

value = value.replaceAll(">", ">");

value = value.replaceAll("'", "'");

value = value.replaceAll(";", "﹔");

value = value.replaceAll("&", "&");

value = value.replaceAll("%", "﹪");

value = value.replaceAll("#", "#");

value = value.replaceAll("select", "seleᴄt");// "c"→"ᴄ"

value = value.replaceAll("truncate", "trunᴄate");// "c"→"ᴄ"

value = value.replaceAll("exec", "exeᴄ");// "c"→"ᴄ"

value = value.replaceAll("join", "jᴏin");// "o"→"ᴏ"

value = value.replaceAll("union", "uniᴏn");// "o"→"ᴏ"

value = value.replaceAll("drop", "drᴏp");// "o"→"ᴏ"

value = value.replaceAll("count", "cᴏunt");// "o"→"ᴏ"

value = value.replaceAll("insert", "ins℮rt");// "e"→"℮"

value = value.replaceAll("update", "updat℮");// "e"→"℮"

value = value.replaceAll("delete", "delet℮");// "e"→"℮"

value = value.replaceAll("script", "sᴄript");// "c"→"ᴄ"

value = value.replaceAll("cookie", "cᴏᴏkie");// "o"→"ᴏ"

value = value.replaceAll("iframe", "ifram℮");// "e"→"℮"

value = value.replaceAll("onmouseover", "onmouseov℮r");// "e"→"℮"

value = value.replaceAll("onmousemove", "onmousemov℮");// "e"→"℮"*/

return value;

}

}

三. 配置web.xml添加过滤器

http:// XssSqlFilter

com.cn.unit.filter.SafeFilter

XssSqlFilter

/*

配置各节点简单介绍:

节点名

介绍

指定一个过滤器

用于为过滤器指定一个名字,该元素的内容不能为空

指定过滤器的完整的限定类名

为过滤器指定初始化参数。在过滤器中,可以使用FilterConfig接口对象来访问初始化参数

的子元素,指定参数的名字

的子元素,指定参数的值

设置一个Filter所负责拦截的资源。可通过Servlet名称或资源访问的请求路径指定

子元素用于设置filter的注册名称。该值必须是在元素中声明过的过滤器的名字

设置 filter 所拦截的请求路径(过滤器关联的URL样式)

指定过滤器所拦截的Servlet名称

指定过滤器所拦截的资源被 Servlet 容器调用的方式,默认REQUEST

四. 静态资源跳过过滤

在实际开发的过程中,js、css等静态资源也进行过滤,消耗服务器性能,因此把一些不必要过滤的直接跳过过滤器,实现如下:

4.1 在web.xml配置文件中添加参数,保存静态资源所在的路径

excludeFilter

/document/;/ligentres/

如图:

4.2 过滤器初始化方法,读取静态资源所在的路径

public FilterConfig filterConfig;

public String[] excludeFilterArray;

@Override

public void init(FilterConfig config) throws ServletException {

filterConfig = config;

// 读取web配置文件中的静态资源所在路径

String excludeFilter = filterConfig.getInitParameter("excludeFilter");

excludeFilterArray = excludeFilter.split(";");

}

4.3 过滤器拦截请求,若是静态资源所在的路径直接跳过过滤器

@Override

public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {

HttpServletRequest httpRequest = (HttpServletRequest) request;

String uri = httpRequest.getRequestURI();

// 静态资源直接跳过,不进行过滤

if(uri==null||this.isContains(uri, excludeFilterArray)) {

chain.doFilter(request, response);

return;

}

......

}

// 判断数组是否包含某一元素

public boolean isContains(String uri, String[] regx) {

boolean result = false;

for (int i = 0; i < regx.length; i++) {

if (uri.indexOf(regx[i]) != -1) {

return true;

}

}

return result;

}

五. 大功告成

借鉴前人的经验,一开始测试的时候发现,普通的表单提交和ajax提交可以过滤其参数,但上传文件时就无法进入过滤了。

后来经过研究,用于处理文件上传的 MultipartResolver ,当收到请求时,DispatcherServlet 的 checkMultipart() 方法会调用 MultipartResolver 的 isMultipart() 方法判断请求中是否包含文件。如果请求数据中包含文件,则调用 MultipartResolver 的 resolveMultipart() 方法对请求的数据进行解析,然后将文件数据解析成 MultipartFile 并封装在 MultipartHttpServletRequest 对象中,最后传递给 Controller。因此我们只需要在定义过滤器时,先获取请求头判断是否为文件上传,若是再对数据进行解析便可。


版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:SpringBoot创建多模块项目的全过程记录
下一篇:IDEA配置JRebel实现热部署的方法
相关文章

 发表评论

暂时没有评论,来抢沙发吧~