XSS和SQL注入是Web应用中常见计算机安全漏洞,文章主要分享通过Spring Cloud Gateway 全局过滤器对XSS和SQL注入进行安全防范。
写这篇文章也是因为项目在经过安全组进行安全巡检时发现项目存储该漏洞后进行系统整改,本文的运行结果是经过安全组验证通过。
使用版本
- spring-cloud-dependencies Hoxton.SR7
- spring-boot-dependencies 2.2.9.RELEASE
- spring-cloud-gateway 2.2.4.RELEASE
核心技术点
1. AddRequestParameterGatewayFilterFactory 获取get请求参数并添加参数然后重构get请求
public GatewayFilter apply(NameValueConfig config) {
return new GatewayFilter() {
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
URI uri = exchange.getRequest().getURI();
StringBuilder query = new StringBuilder();
//获取请求url携带的参数,?号后面参数体,类似cl=3&tn=baidutop10&fr=top1000&wd=31
String originalQuery = uri.getRawQuery();
if (StringUtils.hasText(originalQuery)) {
query.append(originalQuery);
if (originalQuery.charAt(originalQuery.length() - 1) != '&') {
query.append('&');
}
}
String value = ServerWebExchangeUtils.expand(exchange, config.getValue());
query.append(config.getName());
query.append('=');
query.append(value);
try {
//重构请求uri
URI newUri = UriComponentsBuilder.fromUri(uri).replaceQuery(query.toString()).build(true).toUri();
ServerHttpRequest request = exchange.getRequest().mutate().uri(newUri).build();
return chain.filter(exchange.mutate().request(request).build());
} catch (RuntimeException var9) {
throw new IllegalStateException("Invalid URI query: \"" + query.toString() + "\"");
}
}
public String toString() {
return GatewayToStringStyler.filterToStringCreator(AddRequestParameterGatewayFilterFactory.this).append(config.getName(), config.getValue()).toString();
}
};
}
2. [Spring Cloud Gateway中RequestBody只能获取一次的问题解决方案](https://blog.youkuaiyun.com/dear_little_bear/article/details/105319657)
3. Spring Gateway GlobalFilter
技术实现
- 创建Filter 实现GlobalFilter, Ordered
@Slf4j
@Component
public class SqLinjectionFilter implements GlobalFilter, Ordered {
@SneakyThrows
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain){
// grab configuration from Config object
log.debug("----自定义防XSS攻击网关全局过滤器生效----");
ServerHttpRequest serverHttpRequest = exchange.getRequest();
HttpMethod method = serverHttpRequest.getMethod();
String contentType = serverHttpRequest.getHeaders().getFirst(HttpHeaders.CONTENT_TYPE);
URI uri = exchange.getRequest().getURI();
Boolean postFlag = (method == HttpMethod.POST || method == HttpMethod.PUT) &&
(MediaType.APPLICATION_FORM_URLENCODED_VALUE.equalsIgnoreCase(contentType) || MediaType.APPLICATION_JSON_VALUE.equals(contentType));
//过滤get请求
if (method == HttpMethod.GET) {
String rawQuery = uri.getRawQuery();
if (StringUtils.isBlank(rawQuery)){
return chain.filter(exchange);
}
log.debug("原请求参数为:{}", rawQuery);
// 执行XSS清理
rawQuery = XssCleanRuleUtils.xssGetClean(rawQuery);
log.debug("修改后参数为:{}", rawQuery);
// 如果存在sql注入,直接拦截请求
if (rawQuery.contains("forbid")) {
log.error("请求【" + uri.getRawPath() + uri.getRawQuery() + "】参数中包含不允许sql的关键词, 请求拒绝");
return setUnauthorizedResponse(exchange);
}
try {
//重新构造get request
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) {
log.error("get请求清理xss攻击异常", e);
throw new IllegalStateException("Invalid URI query: \"" + rawQuery + "\"");
}
}
//post请求时,如果是文件上传之类的请求,不修改请求消息体
else if (postFlag){
return DataBufferUtils.join(serverHttpRequest.getBody()).flatMap(d -> Mon