漏洞修复
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