漏洞修复

This commit is contained in:
itcast 2025-10-24 16:53:08 +08:00
parent 341990088e
commit bb962793fd
2 changed files with 175 additions and 0 deletions

64
node_modules/lodash/package.json generated vendored Normal file
View File

@ -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"
}

View File

@ -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;
}
}