彻底解决Spring MVC XSS注入问题
来源:互联网 发布:smt自动点胶机编程 编辑:程序博客网 时间:2024/06/05 19:21
一、背景
最近所从事的项目,线上被扫描出部分连接存在XSS注入问题。例如:
http://www.xxx.com/yyy.html?applyId=5a20c06fa15243c109b66bb8%22%3E%3Csvg/onload=alert(1)%3E&cate=&channel=0&isEmbed=0&level=0
上面连接中的 alert(1)脚本被执行。存在XSS漏洞。接下来开始解决,经过一个曲折的过程终于找到一个最佳方法。
二、可能的方案
1) 在每个Controller入口的业务代码处手动进行过滤,如:
@RequestMapping("pcDetail.html") public @XssCheck ModelAndView pcDetail(Integer tempId, String applyId) { applyId = HtmlUtils.htmlEscape(applyId); }
这种方法最直接,但也最低级、最繁琐,每个入口都必须加一个过滤。
2) 百度上搜索 Spring mvc XSS关键词,出现最多的方案是 给Servlet加Filter,大致思路是:
包装request->创建过滤器->添加过滤器
通过扩展HttpServletRequestWrapper,对HttpServletRequest进行二次包装,覆盖其 public String[] getParameterValues(String name) 方法,在此方法中对各个参数值进行XSS过滤(Spring MVC 部分解析是调用的此方法)
但这种方法有个缺点:只能过滤GET请求,对于POST请求无能为力。对于POST请求,则还需要对 request.getInputStream的内容进行过滤(比较麻烦)。
笔者没有采用这种方式,继续寻找更优方案。。。
3)在Spring MVC流程中解决,能过自定义实现HandlerMethodArgumentResolver接口来自定义解析请求参数,在解析时做XSS过滤。
这种方法的话,解析过程比较繁琐、复杂,要考虑各种各样的客户端请求格式如json,form,xml等等。而且Spring MVC本身已经有非常完备的各种解析实现了。
为了一个XSS过滤又重新写一套,得不尝失。
笔者翻遍了Spring MVC的代码,也尝试过各种扩展,都不太理想。。。最后终于想到一个改动非常小,且可行办法。
三、可行的方案
主要思路是:在Spring MVC调用Controller前,通过动态代理和反射机制对Controller的调用进行拦截,并在挡截中对Mehtod参数的值进行XSS过滤替换。
到这里,可能有人想说,Spring MVC本身就支持Controller拦截,即实现HandlerInterceptorAdapter接口。这种方法不可行,此接口无法实现对请求参数的修改。
话不多说,上代码(笔者基于Spring 3.2.4版本)。
1) 、 HandlerExecutionChainWrapper.java
public class HandlerExecutionChainWrapper extends HandlerExecutionChain {private BeanFactory beanFactory;private HttpServletRequest request;private HandlerMethod handlerWrapper;private byte[] lock = new byte[0];public HandlerExecutionChainWrapper(HandlerExecutionChain chain,HttpServletRequest request,BeanFactory beanFactory) {super(chain.getHandler(),chain.getInterceptors());this.request = request;this.beanFactory = beanFactory;}@Overridepublic Object getHandler() {if (handlerWrapper != null) {return handlerWrapper;}synchronized (lock) {if (handlerWrapper != null) {return handlerWrapper;}HandlerMethod superMethodHandler = (HandlerMethod)super.getHandler();Object proxyBean = createProxyBean(superMethodHandler);handlerWrapper = new HandlerMethod(proxyBean,superMethodHandler.getMethod());return handlerWrapper;}}/** * 为Controller Bean创建一个代理实例,以便用于 实现调用真实Controller Bean前的切面拦截 * 用以过滤方法参数中可能的XSS注入 * @param handler * @return */private Object createProxyBean(HandlerMethod handler) {try {Enhancer enhancer = new Enhancer();enhancer.setSuperclass(handler.getBeanType());Object bean = handler.getBean();if (bean instanceof String) {bean = beanFactory.getBean((String)bean);}ControllerXssInterceptor xss = new ControllerXssInterceptor(bean);xss.setRequest(this.request);enhancer.setCallback(xss);return enhancer.create();}catch(Exception e) {throw new IllegalStateException("为Controller创建代理失败:"+e.getMessage(), e);}}public static class ControllerXssInterceptor implements MethodInterceptor {private Object target;private HttpServletRequest request;private List<String> objectMatchPackages;public ControllerXssInterceptor(Object target) {this.target = target;this.objectMatchPackages = new ArrayList<String>();this.objectMatchPackages.add("com.xx");}public void setRequest(HttpServletRequest request) {this.request = request;}@Overridepublic Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {//对Controller的方法参数进行调用前处理//过滤String类型参数中可能存在的XSS注入if (args != null) {for (int i=0;i<args.length;i++) {if (args[i]==null)continue;if (args[i] instanceof String) {args[i] = stringXssReplace((String)args[i]);continue;}for(String pk:objectMatchPackages) {if (args[i].getClass().getName().startsWith(pk)) {objectXssReplace(args[i]);break;}}}}return method.invoke(target, args);}private String stringXssReplace(String argument) {return HtmlUtils.htmlEscape(argument);}private void objectXssReplace(final Object argument) {if (argument == null)return;ReflectionUtils.doWithFields(argument.getClass(), new FieldCallback(){@Overridepublic void doWith(Field field) throws IllegalArgumentException, IllegalAccessException {ReflectionUtils.makeAccessible(field);String fv = (String)field.get(argument);if (fv != null) {String nv = HtmlUtils.htmlEscape(fv);field.set(argument, nv);}}}, new FieldFilter(){@Overridepublic boolean matches(Field field) {boolean typeMatch = String.class.equals(field.getType());if (request!=null && "GET".equals(request.getMethod())) {boolean requMatch = request.getParameterMap().containsKey(field.getName());return typeMatch && requMatch;}return typeMatch;}});}}}
2、DispatcherServletWrapper.java
@SuppressWarnings("serial")public class DispatcherServletWrapper extends DispatcherServlet {@Overrideprotected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {HandlerExecutionChain chain = super.getHandler(request);Object handler = chain.getHandler(); if (!(handler instanceof HandlerMethod)) { return chain; } HandlerMethod hm = (HandlerMethod)handler; if (!hm.getBeanType().isAnnotationPresent(Controller.class)) { return chain; } //本扩展仅处理@Controller注解的Beanreturn new HandlerExecutionChainWrapper(chain,request,getWebApplicationContext());}}
3、替换Spring的DispatcherServlet为DispatcherServletWrapper
<servlet><servlet-name>springmvc</servlet-name><!-- <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> --><servlet-class>com.xx.sdd.mkt.web.spring.DispatcherServletWrapper</servlet-class><init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:spring-config-servlet.xml</param-value></init-param> <load-on-startup>1</load-on-startup></servlet>
大功告成,所有通过RequestMapping注解的Controller类方法的参数值均会被过滤。
- 彻底解决Spring MVC XSS注入问题
- 彻底解决Spring MVC 中文乱码 问题
- 彻底解决Spring MVC 中文乱码 问题
- 彻底解决Spring MVC 中文乱码 问题
- 彻底解决Spring MVC 中文乱码 问题
- 彻底解决Spring MVC 中文乱码 问题
- 彻底解决Spring MVC 中文乱码 问题
- 彻底解决Spring MVC 中文乱码 问题
- 彻底解决Spring MVC 中文乱码 问题
- 彻底解决Spring MVC 中文乱码 问题
- 彻底解决Spring MVC 中文乱码 问题
- 彻底解决Spring MVC 中文乱码 问题
- 彻底解决Spring mvc中文乱码问题
- 彻底解决Spring MVC 中文乱码 问题
- 彻底解决Spring MVC 中文变问号?? 问题
- 彻底解决Spring MVC 中文乱码 问题
- 彻底解决Spring MVC 中文乱码 问题
- 彻底解决Spring MVC 中文乱码 问题
- AB1601中volatile的使用
- Java
- MongoDB 主从复制
- Tensorflow-Go的扩展
- IOC和AOP的个人理解
- 彻底解决Spring MVC XSS注入问题
- JAVA-ACE-架构师系列视频课程- RocketMQ(下)订单实战视频课程
- iOS 11.2 问题未停?用户投诉仍遇到各种奇难杂症!
- MongoDB 切换方案
- CentOS 7.0 防火墙开启/关闭
- 如何防止别人看到网页源代码
- linux 安装sysstat使用iostat、mpstat、sar、sa
- Linux(一)常用命令
- springBoot的restFull