漏洞修复
This commit is contained in:
parent
341990088e
commit
bb962793fd
|
|
@ -0,0 +1,64 @@
|
|||
{
|
||||
"_from": "lodash@latest",
|
||||
"_id": "lodash@4.17.21",
|
||||
"_inBundle": false,
|
||||
"_integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
|
||||
"_location": "/lodash",
|
||||
"_phantomChildren": {},
|
||||
"_requested": {
|
||||
"type": "tag",
|
||||
"registry": true,
|
||||
"raw": "lodash@latest",
|
||||
"name": "lodash",
|
||||
"escapedName": "lodash",
|
||||
"rawSpec": "latest",
|
||||
"saveSpec": null,
|
||||
"fetchSpec": "latest"
|
||||
},
|
||||
"_requiredBy": [
|
||||
"#USER",
|
||||
"/"
|
||||
],
|
||||
"_resolved": "https://registry.npmmirror.com/lodash/-/lodash-4.17.21.tgz",
|
||||
"_shasum": "679591c564c3bffaae8454cf0b3df370c3d6911c",
|
||||
"_spec": "lodash@latest",
|
||||
"_where": "D:\\all\\JavaWeb\\devicesmgt",
|
||||
"author": {
|
||||
"name": "John-David Dalton",
|
||||
"email": "john.david.dalton@gmail.com"
|
||||
},
|
||||
"bugs": {
|
||||
"url": "https://github.com/lodash/lodash/issues"
|
||||
},
|
||||
"bundleDependencies": false,
|
||||
"contributors": [
|
||||
{
|
||||
"name": "John-David Dalton",
|
||||
"email": "john.david.dalton@gmail.com"
|
||||
},
|
||||
{
|
||||
"name": "Mathias Bynens",
|
||||
"email": "mathias@qiwi.be"
|
||||
}
|
||||
],
|
||||
"deprecated": false,
|
||||
"description": "Lodash modular utilities.",
|
||||
"homepage": "https://lodash.com/",
|
||||
"icon": "https://lodash.com/icon.svg",
|
||||
"keywords": [
|
||||
"modules",
|
||||
"stdlib",
|
||||
"util"
|
||||
],
|
||||
"license": "MIT",
|
||||
"main": "lodash.js",
|
||||
"name": "lodash",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/lodash/lodash.git"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "echo \"See https://travis-ci.org/lodash-archive/lodash-cli for testing details.\""
|
||||
},
|
||||
"version": "4.17.21"
|
||||
}
|
||||
|
|
@ -0,0 +1,111 @@
|
|||
package com.bonus.sgzb.gateway.filter;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.cloud.gateway.filter.GlobalFilter;
|
||||
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
import reactor.core.publisher.Mono;
|
||||
import reactor.core.publisher.Flux;
|
||||
import org.springframework.core.io.buffer.DataBuffer;
|
||||
import org.springframework.http.server.reactive.ServerHttpRequest;
|
||||
import org.springframework.http.server.reactive.ServerHttpResponse;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.*;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* 防御 XSLT 注入 Gateway 过滤器
|
||||
* 校验 Query / Form / JSON 中指定参数,白名单 + 关键字双重过滤
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
public class XsltInjectionGatewayFilter implements GlobalFilter, Ordered {
|
||||
|
||||
/* 仅允许字母、数字、_- */
|
||||
private static final Pattern WHITE = Pattern.compile("^[A-Za-z0-9_-]+$");
|
||||
|
||||
/* XSLT 危险关键字(大小写不敏感) */
|
||||
private static final Set<String> BLACK_KEYS = Collections.unmodifiableSet(
|
||||
new HashSet<>(Arrays.asList(
|
||||
"xsl:", "<?xml", "system-property", "document(", "unparsed-text",
|
||||
"<![cdata[", "xsl:script", "xsl:eval", "xmlns:xsl=","xsl:include", "xsl:import", "exslt:"
|
||||
))
|
||||
);
|
||||
|
||||
/* 需要校验的参数名 */
|
||||
private static final Set<String> CHECK_PARAMS = Collections.unmodifiableSet(
|
||||
new HashSet<>(Arrays.asList("type", "xsl", "xml", "stylesheet", "transform"))
|
||||
);
|
||||
|
||||
@Override
|
||||
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
|
||||
ServerHttpRequest request = exchange.getRequest();
|
||||
String path = request.getPath().value();
|
||||
|
||||
/* 1. 校验 Query 参数 */
|
||||
MultiValueMap<String, String> queryParams = request.getQueryParams();
|
||||
for (String key : CHECK_PARAMS) {
|
||||
List<String> values = queryParams.get(key);
|
||||
if (values != null) {
|
||||
for (String v : values) {
|
||||
if (!isValid(v)) {
|
||||
log.warn("XSLT_INJECTION_QUERY ip={} uri={} param={} value={}",
|
||||
request.getRemoteAddress(), path, key, v);
|
||||
return reject(exchange);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* 2. 校验 Form 参数(application/x-www-form-urlencoded) */
|
||||
MediaType contentType = request.getHeaders().getContentType();
|
||||
if (MediaType.APPLICATION_FORM_URLENCODED.isCompatibleWith(contentType)) {
|
||||
return exchange.getFormData()
|
||||
.flatMap(form -> {
|
||||
for (String key : CHECK_PARAMS) {
|
||||
List<String> values = form.get(key);
|
||||
if (values != null) {
|
||||
for (String v : values) {
|
||||
if (!isValid(v)) {
|
||||
log.warn("XSLT_INJECTION_FORM ip={} uri={} param={} value={}",
|
||||
request.getRemoteAddress(), path, key, v);
|
||||
return reject(exchange);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return chain.filter(exchange);
|
||||
});
|
||||
}
|
||||
|
||||
/* 3. JSON 参数留扩展(可继续读 body 校验) */
|
||||
return chain.filter(exchange);
|
||||
}
|
||||
|
||||
private boolean isValid(String value) {
|
||||
if (value == null || value.isEmpty()) return true;
|
||||
if (!WHITE.matcher(value).matches()) return false;
|
||||
String lower = value.toLowerCase(Locale.ROOT);
|
||||
return BLACK_KEYS.stream().noneMatch(lower::contains);
|
||||
}
|
||||
|
||||
private Mono<Void> reject(ServerWebExchange exchange) {
|
||||
ServerHttpResponse resp = exchange.getResponse();
|
||||
resp.setStatusCode(HttpStatus.BAD_REQUEST);
|
||||
byte[] body = "Illegal XSLT keyword/character in parameter".getBytes(StandardCharsets.UTF_8);
|
||||
DataBuffer buffer = resp.bufferFactory().wrap(body);
|
||||
return resp.writeWith(Flux.just(buffer));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getOrder() {
|
||||
/* 比 SecurityHeaderFilter 更早执行,先拦非法参数再加头 */
|
||||
return Ordered.HIGHEST_PRECEDENCE;
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue