springboot项目如何防止XSS攻击
作者:细肉云吞 发布时间:2021-10-17 10:03:02
目录
1. 什么是XSS攻击?
2. 如何防范?
2.1 什么时候注入请求参数
3. 具体处理细节
1. 什么是XSS攻击?
XSS攻击全称跨站脚本攻击,是一种在web应用中的计算机安全漏洞,它允许恶意web用户将代码植入到提供给其它用户使用的页面中。也就是作恶的用户通过表单提交一些前端代码,如果不做处理的话,这些前端代码将会在展示的时候被浏览器执行。
2. 如何防范?
有两种方式,一种是一些特殊字符转义,另一种是去除一些危险html元素。本文通过JSOUP库实现第二种方式。
现在问题是,在什么时候对XSS攻击的字符串进行处理呢?这就涉及到spring mvc的Controller中的method params什么时候注入的问题。下面对方法参数注入的原理进行简单说明。
2.1 什么时候注入请求参数
常见的控制器方法有下面几种,分别是通过GET获取请求参数,POST获取json或者form表单的参数
/**
* 通过url的path,获取请求参数
*/
@ResponseBody
@GetMapping("/xssByGetUsingPath/{content}")
public String testGetUsingPath(@PathVariable(name = "content") String content) {
return content;
}
/**
* 通过url的query params获取请求数据. 方法使用简单类型进行接收
*/
@ResponseBody
@GetMapping("/xssByGetUsingSimple")
public String testUsingSimple(@RequestParam(name = "content") String content) {
return content;
}
/**
* 通过url的query params获取请求数据. 方法使用model进行接收
*/
@ResponseBody
@GetMapping("/xssByGetUsingModel")
public String testGetUsingModel(BaseTest.Paper paper) {
return paper.getContent();
}
/**
* 通过 form 表单的方式获取数据. 方法使用简单类型进行接收
*/
@ResponseBody
@PostMapping("/xssByFormPostUsingSimple")
public String testFormPostUsingSimple(@RequestParam(name = "content") String content) {
return content;
}
/**
* 通过 form 表单的方式获取数据. 方法使用model进行参数接收
*/
@ResponseBody
@PostMapping("/xssByFormPostUsingModel")
public String testFormPostUsingModel(BaseTest.Paper paper) {
return paper.getContent();
}
/**
* 通过 request body 发送 json数据
*/
@ResponseBody
@PostMapping("/xssByPostJsonBody")
public String testPostJsonBody(@RequestBody BaseTest.Paper paper) {
return paper.getContent();
}
大家都知道,在spring mvc中处理请求的入口在 DispatcherServlet 类中,其中 doDispatch() 方法完成所有核心功能。在该主流程中,HandlerAdapter 将会对HTTP请求进行 Controller 的方法调用,以及对请求结果进行转换,并封装为DispatcherServlet 类需要的 ModelAndView 。在这里,由于使用注解的方式进行 Controller 定义,所以 HandlerAdapter 的实现类为 RequestMappingHandlerAdapter 。RequestMappingHandlerAdapter 类的 handle() 方法中,委托给 HandlerMethodArgumentResolver 对每个 Controller 的 方法的每个参数进行解析,反射调用 Controller 的 方法后,再 委托 HandlerMethodReturnValueHandler 对反射调用的返回值进行处理。
至此,本文的主角出现了,也就是 HandlerMethodArgumentResolver 。我们看下有关于本文的HandlerMethodArgumentResolver 类继承关系。
上面的常见controller定义方法的 参数解析(注意,这里是说方法那里的参数解析) 对应 HandlerMethodArgumentResolver 的关系如下图:
现在,我们已经知道了他们都由什么样 HandlerMethodArgumentResolver 解析方法参数,我们继续分析他们之前的共同点,并得到在哪里对http传过来的数据进行XSS处理。
RequestResponseBodyMethodProcessor 是一个对request body的JSON数据反序列化的处理器,在 resolveArgument() 方法中,将会获取合适的 org.springframework.http.converter.HttpMessageConverter 类对string数据进行反序列化处理。springboot 在初始化的时候,已经默认注册了 jackson 的 MappingJackson2HttpMessageConverter ,并使用它对 JSON 数据进行反序列化操作。在 jackson 里面,JsonDeserializer 类 用于json数据的property的反序列化,因此,我们可以通过扩展 JsonDeserializer ,并在里面处理XSS即可。
对于 PathVariableMethodArgumentResolver、 RequestParamMethodArgumentResolver、 ServletModelAttributeMethodProcessor ,在 resolveArgument() 方法中,他们对需要对请求参数调用 DataBinder 类 对获取到的参数类型转换和数据绑定。对于类型转换的过程,他们会使用 org.springframework.core.convert.converter.Converter 进行转换。同样,在SPRINGBOOT初始化的过程,也注册了很多个默认的转换器,我们可以注册一个自定义转换器,用于对数据进行xss处理。
3. 具体处理细节
抽象XSSCleaner,用于对string进行XSS处理
public interface XssCleaner {
/**
* 清理 html, 防止XSS
*
* @param html html
* @return 清理后的数据
*/
String clean(String html);
}
使用JSOUP,做HTML节点的白名单处理
public class DefaultXssCleaner implements XssCleaner {
public static final HtmlWhitelist WHITE_LIST = new HtmlWhitelist();
@Override
public String clean(String html) {
if (StringUtils.hasText(html)) {
return Jsoup.clean(html, WHITE_LIST);
}
return html;
}
private static class HtmlWhitelist extends Whitelist {
public HtmlWhitelist() {
//定义标签和属性的白名单
addTags("a", "b", "blockquote", "br", "caption", "cite", "code", "col", "colgroup", "dd", "div", "span", "embed", "object", "dl", "dt",
"em", "h1", "h2", "h3", "h4", "h5", "h6", "i", "img", "li", "ol", "p", "pre", "q", "small",
"strike", "strong", "sub", "sup", "table", "tbody", "td", "tfoot", "th", "thead", "tr", "u", "ul");
addAttributes("a", "href", "title", "target");
addAttributes("blockquote", "cite");
addAttributes("col", "span");
addAttributes("colgroup", "span");
addAttributes("img", "align", "alt", "src", "title");
addAttributes("ol", "start");
addAttributes("q", "cite");
addAttributes("table", "summary");
addAttributes("td", "abbr", "axis", "colspan", "rowspan", "width");
addAttributes("th", "abbr", "axis", "colspan", "rowspan", "scope", "width");
addAttributes("video", "src", "autoplay", "controls", "loop", "muted", "poster", "preload");
addAttributes("object", "width", "height", "classid", "codebase");
addAttributes("param", "name", "value");
addAttributes("embed", "src", "quality", "width", "height", "allowFullScreen", "allowScriptAccess", "flashvars", "name", "type", "pluginspage");
addAttributes(":all", "class", "style", "height", "width", "type", "id", "name");
addProtocols("blockquote", "cite", "http", "https");
addProtocols("cite", "cite", "http", "https");
addProtocols("q", "cite", "http", "https");
}
@Override
protected boolean isSafeAttribute(String tagName, Element el, Attribute attr) {
//不允许 javascript 开头的 src 和 href
if ("src".equalsIgnoreCase(attr.getKey()) || "href".equalsIgnoreCase(attr.getKey())) {
String value = attr.getValue();
if (StringUtils.hasText(value) && value.toLowerCase().startsWith("javascript")) {
return false;
}
}
//允许 base64 的图片内容
if ("img".equals(tagName) && "src".equals(attr.getKey()) && attr.getValue().startsWith("data:;base64")) {
return true;
}
return super.isSafeAttribute(tagName, el, attr);
}
}
}
新增一个jackson的JsonDeserializer
/**
* jackson的反序列化时的html xss过滤器
*/
public class JacksonXssCleanJsonDeserializer extends JsonDeserializer<String> {
private final static Logger LOGGER = LoggerFactory.getLogger(JacksonXssCleanJsonDeserializer.class);
private final XssCleaner xssCleaner;
public JacksonXssCleanJsonDeserializer(XssCleaner xssCleaner) {
this.xssCleaner = xssCleaner;
}
@Override
public Class<?> handledType() {
return String.class;
}
@Override
public String deserialize(JsonParser p, DeserializationContext context) throws IOException, JsonProcessingException {
// XSS clean
String text = p.getValueAsString();
if (StringUtils.hasText(text) && XssCleanMarker.shouldClean()) {
String cleanText = xssCleaner.clean(text);
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Json property value: [{}] cleaned up by JacksonXssCleanJsonDeserializer, current value is:{}.", text, cleanText);
}
return cleanText;
}
return text;
}
}
新增 Converter
/**
* 对请求数据过滤xss
*/
public class XssCleanConverter implements Converter<String, String> {
private final Logger LOGGER = LoggerFactory.getLogger(XssCleanConverter.class);
private XssCleaner xssCleaner;
public XssCleanConverter(XssCleaner xssCleaner) {
this.xssCleaner = xssCleaner;
}
@Override
public String convert(String text) {
if (StringUtils.hasText(text) && XssCleanMarker.shouldClean()) {
String cleanText = xssCleaner.clean(text);
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("request param [{}] cleaned up by XssCleanConverter, current value is:{}.", text, cleanText);
}
return cleanText;
}
return text;
}
}
对 JsonDeserializer 和 Converter 进行注册
@Configuration
@ConditionalOnProperty(value = XSS_PROPERTIES_ENABLED, havingValue = "true", matchIfMissing = true)
@ConditionalOnWebApplication
@EnableConfigurationProperties({XssProperties.class})
public class WebXssConfiguration implements WebMvcConfigurer {
private XssProperties properties;
public WebXssConfiguration(XssProperties properties) {
this.properties = properties;
}
@Bean
@ConditionalOnMissingBean
public XssCleaner xssCleaner() {
return new DefaultXssCleaner();
}
@Bean
@ConditionalOnClass(value = {ObjectMapper.class, Jackson2ObjectMapperBuilder.class})
public Jackson2ObjectMapperBuilderCustomizer jacksonXssCleanJsonDeserializerCustomer(XssCleaner xssCleaner) {
return builder -> builder.deserializers(new JacksonXssCleanJsonDeserializer(xssCleaner));
}
@Override
public void addFormatters(FormatterRegistry registry) {
XssCleaner xssCleaner = xssCleaner();
registry.addConverter(new XssCleanConverter(xssCleaner));
}
@Override
public void addInterceptors(InterceptorRegistry registry) {
XssCleanMarkerHandlerInterceptor handlerInterceptor = new XssCleanMarkerHandlerInterceptor(properties);
registry.addInterceptor(handlerInterceptor);
}
}
上面是XSS处理的核心代码。处理XSS处理,还进行了一些扩展,比如 http path 路径的过滤 和 一些使能控制。
完整的代码可以参考仓库:仓库地址
来源:https://my.oschina.net/thinwonton/blog/5086157


猜你喜欢
- 定义:定义一个用于创建对象的接口,让子类决定实例化哪一个类,工厂方法使一个类的实例化延迟到其子类。类型:创建类模式类图:工厂方法模式代码in
- 本文实例为大家分享了java实现银行管理系统的具体代码,供大家参考,具体内容如下Bank类package First;import java
- 1,compareTo(Object o)方法是java.lang.Comparable<T>接口中的方法,当需要对某个类的对象
- #define只加一个参数 的解释<stdio.h> 里有:#ifndef __STDIO_H #define &n
- 1. C#实现复数类我们在进行信号分析的时候,难免会使用到复数。但是遗憾的是,C#没有自带的复数类,以下提供了一种复数类的构建方法。复数相比
- Spring的最基本的能力就是DI,即依赖注入,或控制反转,它可以为Bean注入其依赖的其他Bean。一个Bean依赖其他Bean一般是通过
- 具体代码如下所示:import java.io.File;public class Scan { public static v
- 首先感谢:http://www.codeproject.com/Articles/25487/Cryptographic-Interoper
- 1、原来是将EditView放到了popupwindow,发现EditView原有的复制、粘贴、全选、选择功能失效了,所以便用DialogF
- 这个东西我已经用了有段时间了,从开始写文章就在用这个,主要原因还是因为我比较懒。懒得去寻找图片,同时又怕万一惹来版权争议。。。跟我所有的文章
- 一、BufferedImage类介绍生成验证码图片主要用到了一个BufferedImage类,如下:创建一个DrawImage Servle
- Android仿360悬浮小球自定义view实现示例效果图如下:实现当前这种类似的效果 和360小球 悬浮桌面差不错类似。这种效果是如何实现
- C#获取远程图片,需要Form用户名和密码的Authorization认证using System;using System.Collect
- 本文实例讲述了Android编程实现全局获取Context及使用Intent传递对象的方法。分享给大家供大家参考,具体如下:一、全局获取 C
- 题目描述:给定一 m*n 的矩阵,请按照逆时针螺旋顺序,返回矩阵中所有元素。示例:思路:这是一道典型的模拟问题:我们可以分析一下,遍历前进轨
- layout布局<RelativeLayout xmlns:android="http://schemas.android.
- 1.进入<Android_Source_Path>/build/target/product/security,找到【platf
- boolean isGBK(String s) throws UnsupportedEncodingException { if(s.equ
- 1 前言在前文中,已经讲述了 AOP 的后置处理器使用和方法,在本文中继续分享增强信息相关的源码,这里才是 AOP 的核心代码。2 spri
- 最近项目上需要实现蓝牙传输apk的一个功能,能够搜索周围的蓝牙手机并分享文件。从需求上讲android手机自带的蓝牙传输模块就可以满足需要了