This is an automated email from the ASF dual-hosted git repository.
Aias00 pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/shenyu.git
The following commit(s) were added to refs/heads/master by this push:
new 55fcd1c6fc fix(sign): reject malformed authorization parameters
(#6475) (#7633)
55fcd1c6fc is described below
commit 55fcd1c6fc58c3f89757cb8514cb7f2ca6a0abe0
Author: JerryčAI <[email protected]>
AuthorDate: Thu Oct 15 11:20:30 2026 +0800
fix(sign): reject malformed authorization parameters (#6475) (#7633)
Signed-off-by: jerry <[email protected]>
---
.../plugin/sign/extractor/VersionTwoExtractor.java | 36 ++++++++++++-------
.../plugin/sign/service/ComposableSignService.java | 8 ++++-
.../apache/shenyu/plugin/sign/SignPluginTest.java | 42 ++++++++++++++++++++++
.../sign/extractor/VersionTwoExtractorTest.java | 19 ++++++++++
4 files changed, 91 insertions(+), 14 deletions(-)
diff --git
a/shenyu-plugin/shenyu-plugin-security/shenyu-plugin-sign/src/main/java/org/apache/shenyu/plugin/sign/extractor/VersionTwoExtractor.java
b/shenyu-plugin/shenyu-plugin-security/shenyu-plugin-sign/src/main/java/org/apache/shenyu/plugin/sign/extractor/VersionTwoExtractor.java
index 43f08ec8fb..b71b3cf7e4 100644
---
a/shenyu-plugin/shenyu-plugin-security/shenyu-plugin-sign/src/main/java/org/apache/shenyu/plugin/sign/extractor/VersionTwoExtractor.java
+++
b/shenyu-plugin/shenyu-plugin-security/shenyu-plugin-sign/src/main/java/org/apache/shenyu/plugin/sign/extractor/VersionTwoExtractor.java
@@ -24,8 +24,10 @@ import org.apache.shenyu.plugin.sign.api.SignParameters;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpRequest;
+import java.nio.charset.StandardCharsets;
import java.util.Base64;
import java.util.Map;
+import java.util.Objects;
import java.util.Optional;
import static
org.apache.shenyu.plugin.sign.extractor.DefaultExtractor.VERSION_2;
@@ -42,22 +44,30 @@ public class VersionTwoExtractor implements
SignParameterExtractor {
if (StringUtils.isEmpty(token) || !token.contains(".")) {
return new SignParameters();
}
- String[] tokenArray = StringUtils.split(token, '.');
+ String[] tokenArray = token.split("\\.", -1);
+ if (tokenArray.length != 2 || StringUtils.isAnyBlank(tokenArray)) {
+ return new SignParameters();
+ }
String parameters = tokenArray[0];
String signature = tokenArray[1];
- Map<String, Object> headerMap = JsonUtils.jsonToMap(new
String(Base64.getDecoder().decode(parameters)));
-
- SignParameters signParameters = new SignParameters(
- VERSION_2,
- (String) headerMap.get(Constants.APP_KEY),
- (String) headerMap.get(Constants.TIMESTAMP),
- signature,
- httpRequest.getURI(),
- (String) headerMap.get("alg"));
- signParameters.setParameters(parameters);
-
- return signParameters;
+ try {
+ Map<String, Object> headerMap = JsonUtils.jsonToMap(new
String(Base64.getDecoder().decode(parameters), StandardCharsets.UTF_8));
+ if (Objects.isNull(headerMap) || headerMap.isEmpty()) {
+ return new SignParameters();
+ }
+ SignParameters signParameters = new SignParameters(
+ VERSION_2,
+ (String) headerMap.get(Constants.APP_KEY),
+ (String) headerMap.get(Constants.TIMESTAMP),
+ signature,
+ httpRequest.getURI(),
+ (String) headerMap.get("alg"));
+ signParameters.setParameters(parameters);
+ return signParameters;
+ } catch (IllegalArgumentException | ClassCastException e) {
+ return new SignParameters();
+ }
}
private String getDefaultToken(final HttpRequest httpRequest) {
diff --git
a/shenyu-plugin/shenyu-plugin-security/shenyu-plugin-sign/src/main/java/org/apache/shenyu/plugin/sign/service/ComposableSignService.java
b/shenyu-plugin/shenyu-plugin-security/shenyu-plugin-sign/src/main/java/org/apache/shenyu/plugin/sign/service/ComposableSignService.java
index 1de1a1bbce..17e8e4e1b0 100644
---
a/shenyu-plugin/shenyu-plugin-security/shenyu-plugin-sign/src/main/java/org/apache/shenyu/plugin/sign/service/ComposableSignService.java
+++
b/shenyu-plugin/shenyu-plugin-security/shenyu-plugin-sign/src/main/java/org/apache/shenyu/plugin/sign/service/ComposableSignService.java
@@ -181,7 +181,13 @@ public class ComposableSignService implements SignService {
}
private VerifyResult verifyExpires(final SignParameters signParameters) {
- final LocalDateTime start =
DateUtils.formatLocalDateTimeFromTimestampBySystemTimezone(Long.parseLong(signParameters.getTimestamp()));
+ final long timestamp;
+ try {
+ timestamp = Long.parseLong(signParameters.getTimestamp());
+ } catch (NumberFormatException e) {
+ return VerifyResult.fail(Constants.SIGN_PARAMS_ERROR);
+ }
+ final LocalDateTime start =
DateUtils.formatLocalDateTimeFromTimestampBySystemTimezone(timestamp);
final LocalDateTime now = LocalDateTime.now();
final long between = DateUtils.acquireMinutesBetween(start, now);
if (Math.abs(between) <= delay) {
diff --git
a/shenyu-plugin/shenyu-plugin-security/shenyu-plugin-sign/src/test/java/org/apache/shenyu/plugin/sign/SignPluginTest.java
b/shenyu-plugin/shenyu-plugin-security/shenyu-plugin-sign/src/test/java/org/apache/shenyu/plugin/sign/SignPluginTest.java
index f78ae9bd20..64268f42b7 100644
---
a/shenyu-plugin/shenyu-plugin-security/shenyu-plugin-sign/src/test/java/org/apache/shenyu/plugin/sign/SignPluginTest.java
+++
b/shenyu-plugin/shenyu-plugin-security/shenyu-plugin-sign/src/test/java/org/apache/shenyu/plugin/sign/SignPluginTest.java
@@ -17,13 +17,18 @@
package org.apache.shenyu.plugin.sign;
+import org.apache.shenyu.common.constant.Constants;
import org.apache.shenyu.common.dto.RuleData;
import org.apache.shenyu.common.dto.SelectorData;
import org.apache.shenyu.common.enums.PluginEnum;
import org.apache.shenyu.plugin.api.ShenyuPluginChain;
+import org.apache.shenyu.plugin.api.context.ShenyuContext;
import org.apache.shenyu.plugin.api.result.DefaultShenyuResult;
import org.apache.shenyu.plugin.api.result.ShenyuResult;
import org.apache.shenyu.plugin.api.utils.SpringBeanUtils;
+import org.apache.shenyu.plugin.sign.extractor.DefaultExtractor;
+import org.apache.shenyu.plugin.sign.provider.DefaultSignProvider;
+import org.apache.shenyu.plugin.sign.service.ComposableSignService;
import org.apache.shenyu.plugin.sign.service.SignService;
import org.apache.shenyu.plugin.sign.api.VerifyResult;
import org.apache.shenyu.plugin.sign.handler.SignPluginDataHandler;
@@ -32,6 +37,8 @@ import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.ValueSource;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.mockito.junit.jupiter.MockitoSettings;
@@ -53,6 +60,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verifyNoInteractions;
import static org.mockito.Mockito.when;
/**
@@ -97,6 +105,40 @@ public final class SignPluginTest {
assertTrue(signRuleHandler.getSignRequestBody());
}
+ @ParameterizedTest
+ @ValueSource(booleans = {false, true})
+ public void testMalformedAuthorizationIsRejected(final boolean signBody) {
+ MockServerWebExchange malformedExchange =
MockServerWebExchange.from(MockServerHttpRequest.post("/test")
+ .header(Constants.VERSION, DefaultExtractor.VERSION_2)
+ .header(Constants.SHENYU_AUTHORIZATION, "%%%.signature")
+ .contentType(MediaType.APPLICATION_JSON).body("{}"));
+ assertMalformedInputRejected(malformedExchange, signBody);
+ }
+
+ @ParameterizedTest
+ @ValueSource(strings = {"not-a-number", "9223372036854775808"})
+ public void testMalformedTimestampIsRejected(final String timestamp) {
+ MockServerWebExchange malformedExchange =
MockServerWebExchange.from(MockServerHttpRequest.get("/test")
+ .header(Constants.VERSION, DefaultExtractor.VERSION_1)
+ .header(Constants.APP_KEY, "app-key")
+ .header(Constants.TIMESTAMP, timestamp)
+ .header(Constants.SIGN, "signature").build());
+ assertMalformedInputRejected(malformedExchange, false);
+ }
+
+ private void assertMalformedInputRejected(final MockServerWebExchange
malformedExchange, final boolean signBody) {
+ malformedExchange.getAttributes().put(Constants.CONTEXT, new
ShenyuContext());
+ ruleData.setHandle("{\"signRequestBody\": " + signBody + "}");
+ signPluginDataHandler.handlerRule(ruleData);
+ SignPlugin plugin = new
SignPlugin(HandlerStrategies.withDefaults().messageReaders(),
+ new ComposableSignService(new DefaultExtractor(), new
DefaultSignProvider()));
+
+ StepVerifier.create(plugin.doExecute(malformedExchange, chain, new
SelectorData(), ruleData)).verifyComplete();
+
+
assertTrue(malformedExchange.getResponse().getBodyAsString().block().contains(Constants.SIGN_PARAMS_ERROR));
+ verifyNoInteractions(chain);
+ }
+
@Test
public void testSignPluginSimple() {
this.exchange =
MockServerWebExchange.from(MockServerHttpRequest.get("localhost").build());
diff --git
a/shenyu-plugin/shenyu-plugin-security/shenyu-plugin-sign/src/test/java/org/apache/shenyu/plugin/sign/extractor/VersionTwoExtractorTest.java
b/shenyu-plugin/shenyu-plugin-security/shenyu-plugin-sign/src/test/java/org/apache/shenyu/plugin/sign/extractor/VersionTwoExtractorTest.java
index 9d1d9d8e25..45dc899412 100644
---
a/shenyu-plugin/shenyu-plugin-security/shenyu-plugin-sign/src/test/java/org/apache/shenyu/plugin/sign/extractor/VersionTwoExtractorTest.java
+++
b/shenyu-plugin/shenyu-plugin-security/shenyu-plugin-sign/src/test/java/org/apache/shenyu/plugin/sign/extractor/VersionTwoExtractorTest.java
@@ -21,6 +21,8 @@ import org.apache.shenyu.common.constant.Constants;
import org.apache.shenyu.common.utils.JsonUtils;
import org.apache.shenyu.plugin.sign.api.SignParameters;
import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.ValueSource;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpRequest;
import org.springframework.mock.http.server.reactive.MockServerHttpRequest;
@@ -86,6 +88,23 @@ final class VersionTwoExtractorTest {
assertEmpty(actual);
}
+ @ParameterizedTest
+ @ValueSource(strings = {".", "header.", ".signature",
"header.signature.extra", "%%%.signature"})
+ void testMalformedToken(final String token) {
+ HttpRequest request = MockServerHttpRequest.get("/api/orders")
+ .header(Constants.SHENYU_AUTHORIZATION, token).build();
+ assertEmpty(extractor.extract(request));
+ }
+
+ @ParameterizedTest
+ @ValueSource(strings = {"null", "[]", "{", "{\"appKey\":123}",
"{\"timestamp\":123}", "{\"alg\":[]}"})
+ void testMalformedParameters(final String json) {
+ String token =
Base64.getEncoder().encodeToString(json.getBytes(StandardCharsets.UTF_8)) +
".signature";
+ HttpRequest request = MockServerHttpRequest.get("/api/orders")
+ .header(Constants.SHENYU_AUTHORIZATION, token).build();
+ assertEmpty(extractor.extract(request));
+ }
+
private String parameters(final String appKey, final String timestamp,
final String algorithm) {
Map<String, String> values = new HashMap<>();
values.put(Constants.APP_KEY, appKey);