赞
踩
跨站脚本攻击XSS(Cross Site Scripting),为了不和层叠样式表(Cascading Style Sheets, CSS)的缩写混淆,故将跨站脚本攻击缩写为XSS。恶意攻击者往Web页面里插入恶意Script代码,当用户浏览该页之时,嵌入其中Web里面的Script代码会被执行,从而达到恶意攻击用户的目的。XSS攻击针对的是用户层面的攻击!
XSS分为:存储型 、反射型 、DOM型XSS
存储型XSS:存储型XSS,持久化,代码是存储在服务器中的,如在个人信息或发表文章等地方,插入代码,如果没有过滤或过滤不严,那么这些代码将储存到服务器中,用户访问该页面的时候触发代码执行。这种XSS比较危险,容易造成蠕虫,盗窃cookie
反射型XSS:非持久化,需要欺骗用户自己去点击链接才能触发XSS代码(服务器中没有这样的页面和内容),一般容易出现在搜索页面
DOM型XSS:不经过后端,DOM-XSS漏洞是基于文档对象模型(Document Objeet Model,DOM)的一种漏洞,DOM-XSS是通过url传入参数去控制触发的,其实也属于反射型XSS。
从以上我们可以知道,存储型的XSS危害最大。因为他存储在服务器端,所以不需要我们和被攻击者有任何接触,只要被攻击者访问了该页面就会遭受攻击。而反射型和DOM型的XSS则需要我们去诱使用户点击我们构造的恶意的URL,需要我们和用户有直接或者间接的接触,比如利用社会工程学或者利用在其他网页挂马的方式。
XSS防御的总体思路是:对用户的输入(和URL参数)进行过滤,对输出进行html编码。也就是对用户提交的所有内容进行过滤,对url中的参数进行过滤,过滤掉会导致脚本执行的相关内容;然后对动态输出到页面的内容进行html编码,使脚本无法在浏览器中执行。
对输入的内容进行过滤,可以分为黑名单过滤和白名单过滤。黑名单过滤虽然可以拦截大部分的XSS攻击,但是还是存在被绕过的风险。白名单过滤虽然可以基本杜绝XSS攻击,但是真实环境中一般是不能进行如此严格的白名单过滤的。
对输出进行html编码,就是通过函数,将用户的输入的数据进行html编码,使其不能作为脚本运行。
如下,是使用php中的htmlspecialchars函数对用户输入的name参数进行html编码,将其转换为html实体
#使用htmlspecialchars函数对用户输入的name参数进行html编码,将其转换为html实体
$name = htmlspecialchars( $_GET[ 'name' ] );
很多时候,内部管理网站往往疏于关注安全问题,只是简单的限制访问来源。这种网站往往对XSS 攻击毫无抵抗力,需要多加注意。安全问题需要长期的关注,从来不是一锤子买卖。XSS 攻击相对其他攻击手段更加隐蔽和多变,和业务流程、代码实现都有关系,不存在什么一劳永逸的解决方案。此外,面对XSS,往往要牺牲产品的便利性才能保证完全的安全,如何在安全和便利之间平衡也是一件需要考虑的事情。
web应用开发者注意事项:
代码执行顺序
CacheBodyGlobalFilter—>XssRequestGlobalFilter—>XssResponseGlobalFilter
这个过滤器解决body不能重复读的问题(在低版本的spring-cloud不需要这个过滤器),为后续的XssRequestGlobalFilter重写请求body做准备
package com.xxx.gateway.filter; import org.springframework.cloud.gateway.filter.GatewayFilterChain; import org.springframework.cloud.gateway.filter.GlobalFilter; import org.springframework.core.Ordered; import org.springframework.core.io.buffer.DataBuffer; import org.springframework.core.io.buffer.DataBufferUtils; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpMethod; import org.springframework.http.MediaType; import org.springframework.http.server.reactive.ServerHttpRequest; import org.springframework.http.server.reactive.ServerHttpRequestDecorator; import org.springframework.stereotype.Component; import org.springframework.web.server.ServerWebExchange; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; /** * @Author: * @Description: 这个过滤器解决body不能重复读的问题,为后续的XssRequestGlobalFilter重写post|put请求的body做准备 * @Date: * <p> * 没把body的内容放到attribute中去,因为从attribute取出body内容还是需要强转成 Flux<DataBuffer>,然后转换成String,和直接读取body没有什么区别 */ @Component public class CacheBodyGlobalFilter implements Ordered, GlobalFilter { @Override public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) { HttpMethod method = exchange.getRequest().getMethod(); String contentType = exchange.getRequest().getHeaders().getFirst(HttpHeaders.CONTENT_TYPE); if (method == HttpMethod.POST || method == HttpMethod.PUT) { if (MediaType.APPLICATION_FORM_URLENCODED_VALUE.equalsIgnoreCase(contentType) || MediaType.APPLICATION_JSON_VALUE.equalsIgnoreCase(contentType) || MediaType.APPLICATION_JSON_UTF8_VALUE.equals(contentType)) { return DataBufferUtils.join(exchange.getRequest().getBody()) .flatMap(dataBuffer -> { DataBufferUtils.retain(dataBuffer); Flux<DataBuffer> cachedFlux = Flux .defer(() -> Flux.just(dataBuffer.slice(0, dataBuffer.readableByteCount()))); ServerHttpRequest mutatedRequest = new ServerHttpRequestDecorator( exchange.getRequest()) { @Override public Flux<DataBuffer> getBody() { return cachedFlux; } }; return chain.filter(exchange.mutate().request(mutatedRequest).build()); }); } } return chain.filter(exchange); } @Override public int getOrder() { return Ordered.HIGHEST_PRECEDENCE; } }
自定义防XSS攻击网关全局过滤器。
package com.xxx.gateway.filter; import io.netty.buffer.ByteBufAllocator; import org.apache.commons.lang.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.cloud.gateway.filter.GatewayFilterChain; import org.springframework.cloud.gateway.filter.GlobalFilter; import org.springframework.core.Ordered; import org.springframework.core.io.buffer.DataBuffer; import org.springframework.core.io.buffer.DataBufferUtils; import org.springframework.core.io.buffer.NettyDataBufferFactory; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpMethod; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.http.server.reactive.ServerHttpRequest; import org.springframework.http.server.reactive.ServerHttpRequestDecorator; import org.springframework.http.server.reactive.ServerHttpResponse; import org.springframework.stereotype.Component; import org.springframework.web.server.ServerWebExchange; import org.springframework.web.util.UriComponentsBuilder; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; import java.net.URI; import java.nio.CharBuffer; import java.nio.charset.StandardCharsets; import java.util.concurrent.atomic.AtomicReference; /** * @Author: * @Description: 自定义防XSS攻击网关全局过滤器 * @Date: */ @Component public class XssRequestGlobalFilter implements GlobalFilter, Ordered { private Logger logger = LoggerFactory.getLogger(XssRequestGlobalFilter.class); /** * * @param exchange * @param chain * @return * * get请求参考spring cloud gateway自带过滤器: * @see org.springframework.cloud.gateway.filter.factory.AddRequestParameterGatewayFilterFactory * * post请求参考spring cloud gateway自带过滤器: * @see org.springframework.cloud.gateway.filter.factory.rewrite.ModifyRequestBodyGatewayFilterFactory */ @Override public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain){ // grab configuration from Config object logger.info("----自定义防XSS攻击网关全局过滤器生效----"); String path = exchange.getRequest().getPath().toString(); ServerHttpRequest serverHttpRequest = exchange.getRequest(); HttpMethod method = serverHttpRequest.getMethod(); String contentType = serverHttpRequest.getHeaders().getFirst(HttpHeaders.CONTENT_TYPE); Boolean postFlag = (method == HttpMethod.POST || method == HttpMethod.PUT) && (MediaType.APPLICATION_FORM_URLENCODED_VALUE.equalsIgnoreCase(contentType) || MediaType.APPLICATION_JSON_VALUE.equals(contentType) || MediaType.APPLICATION_JSON_UTF8_VALUE.equals(contentType)); // get 请求, 参考的是 org.springframework.cloud.gateway.filter.factory.AddRequestParameterGatewayFilterFactory if (method == HttpMethod.GET) { URI uri = exchange.getRequest().getURI(); String rawQuery = uri.getRawQuery(); if (StringUtils.isBlank(rawQuery)){ return chain.filter(exchange); } rawQuery = XssCleanRuleUtils.xssClean(rawQuery); try { URI newUri = UriComponentsBuilder.fromUri(uri) .replaceQuery(rawQuery) .build(true) .toUri(); ServerHttpRequest request = exchange.getRequest().mutate() .uri(newUri).build(); return chain.filter(exchange.mutate().request(request).build()); } catch (Exception e) { logger.error("get请求清理xss攻击异常", e); throw new IllegalStateException("Invalid URI query: \"" + rawQuery + "\""); } } //post请求时,如果是文件上传之类的请求,不修改请求消息体 else if (postFlag){ // 参考的是 org.springframework.cloud.gateway.filter.factory.AddRequestParameterGatewayFilterFactory //从请求里获取Post请求体 String bodyStr = resolveBodyFromRequest(serverHttpRequest); // 这种处理方式,必须保证post请求时,原始post表单必须有数据过来,不然会报错 if (StringUtils.isBlank(bodyStr)) { logger.error("请求异常:{} POST请求必须传递参数", serverHttpRequest.getURI().getRawPath()); ServerHttpResponse response = exchange.getResponse(); response.setStatusCode(HttpStatus.BAD_REQUEST); byte[] bytes = "{\"code\":400,\"msg\":\"post data error\"}".getBytes(StandardCharsets.UTF_8); DataBuffer buffer = response.bufferFactory().wrap(bytes); return response.writeWith(Mono.just(buffer)); } //白名单处理(看业务需求) boolean containsTarget = WhiteListUtils.richTextUrls.stream().anyMatch(s -> path.contains(s)); if (containsTarget) { //bodyStr = XssCleanRuleUtils.xssRichTextClean(bodyStr); bodyStr = XssCleanRuleUtils.xssClean2(bodyStr); } else { bodyStr = XssCleanRuleUtils.xssClean(bodyStr); } URI uri = serverHttpRequest.getURI(); URI newUri = UriComponentsBuilder.fromUri(uri).build(true).toUri(); ServerHttpRequest request = exchange.getRequest().mutate().uri(newUri).build(); DataBuffer bodyDataBuffer = stringBuffer(bodyStr); Flux<DataBuffer> bodyFlux = Flux.just(bodyDataBuffer); // 定义新的消息头 HttpHeaders headers = new HttpHeaders(); headers.putAll(exchange.getRequest().getHeaders()); // 由于修改了传递参数,需要重新设置CONTENT_LENGTH,长度是字节长度,不是字符串长度 int length = bodyStr.getBytes().length; headers.remove(HttpHeaders.CONTENT_LENGTH); headers.setContentLength(length); // 设置CONTENT_TYPE if (StringUtils.isNotBlank(contentType)) { headers.set(HttpHeaders.CONTENT_TYPE, contentType); } // 由于post的body只能订阅一次,由于上面代码中已经订阅过一次body。所以要再次封装请求到request才行,不然会报错请求已经订阅过 request = new ServerHttpRequestDecorator(request) { @Override public HttpHeaders getHeaders() { long contentLength = headers.getContentLength(); HttpHeaders httpHeaders = new HttpHeaders(); httpHeaders.putAll(super.getHeaders()); if (contentLength > 0) { httpHeaders.setContentLength(contentLength); } else { // this causes a 'HTTP/1.1 411 Length Required' on httpbin.org httpHeaders.set(HttpHeaders.TRANSFER_ENCODING, "chunked"); } return httpHeaders; } @Override public Flux<DataBuffer> getBody() { return bodyFlux; } }; //封装request,传给下一级 request.mutate().header(HttpHeaders.CONTENT_LENGTH, Integer.toString(bodyStr.length())); return chain.filter(exchange.mutate().request(request).build()); } else { return chain.filter(exchange); } } @Override public int getOrder() { return -90; } /** * 从Flux<DataBuffer>中获取字符串的方法 * @return 请求体 */ private String resolveBodyFromRequest(ServerHttpRequest serverHttpRequest) { //获取请求体 Flux<DataBuffer> body = serverHttpRequest.getBody(); AtomicReference<String> bodyRef = new AtomicReference<>(); body.subscribe(buffer -> { CharBuffer charBuffer = StandardCharsets.UTF_8.decode(buffer.asByteBuffer()); DataBufferUtils.release(buffer); bodyRef.set(charBuffer.toString()); }); //获取request body return bodyRef.get(); } /** * 字符串转DataBuffer * @param value * @return */ private DataBuffer stringBuffer(String value) { byte[] bytes = value.getBytes(StandardCharsets.UTF_8); NettyDataBufferFactory nettyDataBufferFactory = new NettyDataBufferFactory(ByteBufAllocator.DEFAULT); DataBuffer buffer = nettyDataBufferFactory.allocateBuffer(bytes.length); buffer.write(bytes); return buffer; } }
重写Response,防止xss攻击。
package com.xxx.gateway.filter; import org.reactivestreams.Publisher; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.cloud.gateway.filter.GatewayFilterChain; import org.springframework.cloud.gateway.filter.GlobalFilter; import org.springframework.core.Ordered; import org.springframework.core.io.buffer.DataBuffer; import org.springframework.core.io.buffer.DataBufferFactory; import org.springframework.core.io.buffer.DataBufferUtils; import org.springframework.core.io.buffer.DefaultDataBufferFactory; import org.springframework.http.HttpHeaders; import org.springframework.http.MediaType; import org.springframework.http.server.reactive.ServerHttpResponse; import org.springframework.http.server.reactive.ServerHttpResponseDecorator; import org.springframework.stereotype.Component; import org.springframework.web.server.ServerWebExchange; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; import java.nio.charset.Charset; /** * @Author: * @Description: 重写Response,防止xss攻击 * @Date: */ @Component public class XssResponseGlobalFilter implements Ordered, GlobalFilter { private Logger logger = LoggerFactory.getLogger(XssResponseGlobalFilter.class); @Override public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) { //获取请求url String path = exchange.getRequest().getPath().toString(); ServerHttpResponse originalResponse = exchange.getResponse(); DataBufferFactory bufferFactory = originalResponse.bufferFactory(); ServerHttpResponseDecorator decoratedResponse = new ServerHttpResponseDecorator(originalResponse) { @Override public Mono<Void> writeWith(Publisher<? extends DataBuffer> body) { String contentType = getDelegate().getHeaders().getFirst(HttpHeaders.CONTENT_TYPE); Boolean flag = MediaType.APPLICATION_JSON_VALUE.equals(contentType) || MediaType.APPLICATION_JSON_UTF8_VALUE.equals(contentType); if (body instanceof Flux && flag) { Flux<? extends DataBuffer> fluxBody = (Flux<? extends DataBuffer>) body; return super.writeWith(fluxBody.buffer().map(dataBuffer -> { //如果响应过大,会进行截断,出现乱码, //然后看api DefaultDataBufferFactory有个join方法可以合并所有的流,乱码的问题解决 DataBufferFactory dataBufferFactory = new DefaultDataBufferFactory(); DataBuffer join = null; try { join = dataBufferFactory.join(dataBuffer); byte[] content = new byte[join.readableByteCount()]; join.read(content); //释放掉内存 DataBufferUtils.release(join); String result = new String(content, Charset.forName("UTF-8")); //logger.info("result:"+result); //若为带有富文本的接口,走富文本xss过滤 boolean containsTarget = WhiteListUtils.richTextUrls.stream().anyMatch(s -> path.contains(s)); if (containsTarget) { //result = XssCleanRuleUtils.xssRichTextClean(result); result = XssCleanRuleUtils.xssClean2(result); } else { //result就是response的值,对result进行去XSS result = XssCleanRuleUtils.xssClean(result); } byte[] uppedContent = new String(result.getBytes(), Charset.forName("UTF-8")).getBytes(); return bufferFactory.wrap(uppedContent); } catch (Exception e) { // 处理异常,记录日志等 throw e; } finally { if (join != null) { //释放掉内存 DataBufferUtils.release(join); } } })); } // if body is not a flux. never got there. return super.writeWith(body); } }; // replace response with decorator return chain.filter(exchange.mutate().response(decoratedResponse).build()); } @Override public int getOrder() { return -50; } }
上面几个过滤器使用的工具类
后续我自己这边没有用Jsoup了(感觉有坑,就放弃了)
package com.xxx.gateway.filter; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONArray; import com.alibaba.fastjson.JSONObject; import org.jsoup.Jsoup; import org.jsoup.nodes.Document; import org.jsoup.safety.Whitelist; import org.springframework.core.io.ClassPathResource; import java.io.IOException; import java.io.InputStream; import java.util.Iterator; import java.util.regex.Pattern; /** * @Author: * @Description: xss过滤工具 * @Date: */ public class XssCleanRuleUtils { //xss过滤规则(对于script、src及加载事件和弹窗事件的代码块) private final static Pattern[] scriptPatterns = { Pattern.compile("<script>(.*?)</script>", Pattern.CASE_INSENSITIVE), Pattern.compile("src[\r\n]*=[\r\n]*\\\'(.*?)\\\'", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL), Pattern.compile("</script>", Pattern.CASE_INSENSITIVE), Pattern.compile("<script(.*?)>", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL), Pattern.compile("eval\\((.*?)\\)", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL), Pattern.compile("expression\\((.*?)\\)", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL), Pattern.compile("javascript:", Pattern.CASE_INSENSITIVE), Pattern.compile("vbscript:", Pattern.CASE_INSENSITIVE), Pattern.compile("onload(.*?)=", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL) }; //非富文本的 public static String xssClean(String value) { if (value != null) { value = value.replaceAll("\0|\n|\r", ""); for (Pattern pattern : scriptPatterns) { value = pattern.matcher(value).replaceAll(""); } value = value.replaceAll("<", "<").replaceAll(">", ">"); } return value; } //富文本的 public static String xssClean2(String value) { if (value != null) { value = value.replaceAll("\0|\n|\r", ""); for (Pattern pattern : scriptPatterns) { value = pattern.matcher(value).replaceAll(""); } } return value; } //自定义的json白名单 private static final ClassPathResource jsoupWhiteListPathRes = new ClassPathResource("/json/xssWhiteList.json"); //配置过滤化参数, 不对代码进行格式化 private static final Document.OutputSettings outputSettings = new Document.OutputSettings().prettyPrint(false); //富文本的(使用了Jsoup) public static String xssRichTextClean(String value) { // 创建一个自定义的白名单,基于Jsoup的默认白名单 Whitelist customWhitelist = Whitelist.basic(); InputStream whiteConfig = null; try { whiteConfig = jsoupWhiteListPathRes.getInputStream(); } catch (IOException e) { e.printStackTrace(); } if (whiteConfig == null) { throw new RuntimeException("读取jsoup xss 白名单文件失败"); } else { try { JSONObject whiteListJson = JSON.parseObject(whiteConfig, JSONObject.class); //添加标签 addTags JSONArray addTagsJsonArr = whiteListJson.getJSONArray("addTags"); String[] addTagsArr = addTagsJsonArr.toArray(new String[0]); customWhitelist.addTags(addTagsArr); //添加属性 addAttributes JSONArray addAttrJsonArr = whiteListJson.getJSONArray("addAttributes"); Iterator<Object> iter = addAttrJsonArr.iterator(); while (iter.hasNext()) { JSONObject attrJsonObj = (JSONObject) iter.next(); String tag = attrJsonObj.getString("tag"); JSONArray attrJsonArr = attrJsonObj.getJSONArray("attributes"); String[] attrArr = attrJsonArr.toArray(new String[0]); customWhitelist.addAttributes(tag, attrArr); } //添加 addProtocols JSONArray addProtoJsonArr = whiteListJson.getJSONArray("addProtocols"); iter = addProtoJsonArr.iterator(); while (iter.hasNext()) { JSONObject attrJsonObj = (JSONObject) iter.next(); String tag = attrJsonObj.getString("tag"); String attribute = attrJsonObj.getString("attribute"); JSONArray protoJsonArr = attrJsonObj.getJSONArray("protocols"); String[] protocolArr = protoJsonArr.toArray(new String[0]); customWhitelist.addProtocols(tag, attribute, protocolArr); } } catch (IOException e) { e.printStackTrace(); } } value =Jsoup.clean(value, "", customWhitelist, outputSettings); return value; } }
package com.xxx.gateway.filter; import java.util.Arrays; import java.util.List; //白名单 public class WhiteListUtils { //白名单接口 public static final List<String> richTextUrls = Arrays.asList( "/xxx/information/findByUnid", "/xxx/getNoticeAnnouncementDetail"); }
转载:
{ "addTags": [ "p", "strong", "em", "u", "s", "blockquote", "pre", "h1", "h2", "h3", "h4", "h5", "h6", "br", "ol", "li", "ul", "sub", "sup", "span", "iframe", "a", "img" ], "addAttributes": [ { "tag": ":all", "attributes": [ "class", "id", "src", "style" ] }, { "tag": "pre", "attributes": [ "spellcheck" ] }, { "tag": "iframe", "attributes": [ "frameborder", "allowfullscreen", "src" ] }, { "tag": "a", "attributes": [ "rel", "target" ] }, { "tag": "img", "attributes": [ "align", "alt", "height", "src", "title", "width" ] } ], "addProtocols": [ { "tag": "xx", "attribute": "src", "protocols": [ "src", "http", "https", "data" ] } ] }
Jsoup 使用标签 ** 白名单 ** 的机制用来进行防止 XSS 攻击,
假设白名单中只允许 p 标签存在, 此时在一段 HTML 代码中,** 只能存在 p 标签 **,
其他标签将会被清除,只保留被标签所包裹的内容.
转载:https://www.jianshu.com/p/32abc12a175a
项目依赖
jsoup-1.9.2
<dependency>
<groupId>org.jsoup</groupId>
<artifactId>jsoup</artifactId>
<version>1.9.2</version>
</dependency>
jsoup 默认给我们提供了 5 个白名单对象
白名单对象 | 标签 | 说明 |
---|---|---|
none | 无 | 只保留标签内文本内容 |
simpleText | b,em,i,strong,u | 简单的文本标签 |
basic | a,b,blockquote,br,cite,code,dd,dl,dt,em,i,li,ol, p,pre,q,small,span,strike,strong,sub,sup,u,ul | 基本使用的标签 |
basicWithImages | basic 的基础上添加了 img 标签 及 img 标签的 src,align,alt,height,width,title 属性 | 基本使用的加上 img 标签 |
relaxed | a,b,blockquote,br,caption,cite,code,col,colgroup,dd, div,dl,dt,em,h1,h2,h3,h4,h5,h6,i,img,li,ol,p,pre,q,small, span,strike,strong,sub,sup,table,tbody,td,tfoot,th,thead,tr,u,ul | 在 basicWithImages 的基础上又增加了一部分部分标签 |
如果没有图片上传的需求, 使用 basic
, 否则使用 basicWithImages
Jsoup.clean(testHtml, "", whitelist, new Document.OutputSettings().prettyPrint(false));
进行过滤** 以下为实际项目中所使用的工具类 **
import org.jsoup.Jsoup; import org.jsoup.nodes.Document; import org.jsoup.safety.Whitelist; /** * 描述: 过滤 HTML 标签中 XSS 代码 */ public class JsoupUtil { /** * 使用自带的 basicWithImages 白名单 * 允许的便签有 a,b,blockquote,br,cite,code,dd,dl,dt,em,i,li,ol,p,pre,q,small,span,strike,strong,sub,sup,u,ul,img * 以及 a 标签的 href,img 标签的 src,align,alt,height,width,title 属性 */ private static final Whitelist whitelist = Whitelist.basicWithImages(); /** 配置过滤化参数, 不对代码进行格式化 */ private static final Document.OutputSettings outputSettings = new Document.OutputSettings().prettyPrint(false); static { // 富文本编辑时一些样式是使用 style 来进行实现的 // 比如红色字体 style="color:red;" // 所以需要给所有标签添加 style 属性 whitelist.addAttributes(":all", "style"); } public static String clean(String content) { return Jsoup.clean(content, "", whitelist, outputSettings); } }
【1】spring cloud gateway 过滤器防止跨站脚本攻击(存储XSS、反射XSS)
https://blog.csdn.net/qq_26801767/article/details/106235359?utm_medium=distribute.pc_relevant.none-task-blog-2defaultbaidujs_baidulandingword~default-4-106235359-blog-128300551.235v40pc_relevant_3m_sort_dl_base1&spm=1001.2101.3001.4242.3&utm_relevant_index=7
【2】XSS(跨站脚本)漏洞详解之XSS跨站脚本攻击漏洞的解决
http://www.uml.org.cn/safe/202203034.asp
【3】SpringCloud微服务实战——搭建企业级开发框架(五十一):微服务安全加固—自定义Gateway拦截器实现防止SQL注入/XSS攻击
https://blog.csdn.net/wmz1932/article/details/129449794
【4】Spring Cloud Gateway 实现XSS、SQL注入拦截
https://www.jianshu.com/p/17613323463d
【5】SpringBoot针对富文本和非富文本添加xss过滤
https://blog.csdn.net/ChOLg/article/details/119949942
【6】详解Xss 及SpringBoot 防范Xss攻击(附全部代码)
https://www.cnblogs.com/blbl-blog/p/17188558.html
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。