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);

Reply via email to