diff --git a/sa-token-core/src/main/java/cn/dev33/satoken/strategy/SaFirewallStrategy.java b/sa-token-core/src/main/java/cn/dev33/satoken/strategy/SaFirewallStrategy.java index 3cacda3b..c8b9ccce 100644 --- a/sa-token-core/src/main/java/cn/dev33/satoken/strategy/SaFirewallStrategy.java +++ b/sa-token-core/src/main/java/cn/dev33/satoken/strategy/SaFirewallStrategy.java @@ -45,6 +45,7 @@ public final class SaFirewallStrategy { checkHooks.add(SaFirewallCheckHookForWhitePath.instance); checkHooks.add(SaFirewallCheckHookForBlackPath.instance); checkHooks.add(SaFirewallCheckHookForPathDangerCharacter.instance); + checkHooks.add(SaFirewallCheckHookForPathBannedCharacter.instance); checkHooks.add(SaFirewallCheckHookForDirectoryTraversal.instance); checkHooks.add(SaFirewallCheckHookForHost.instance); checkHooks.add(SaFirewallCheckHookForHttpMethod.instance); @@ -52,7 +53,10 @@ public final class SaFirewallStrategy { checkHooks.add(SaFirewallCheckHookForParameter.instance); } - // 注册一个防火墙校验 hook + /** + * 注册一个防火墙校验 hook + * @param checkHook / + */ public void registerCheckHook(SaFirewallCheckHook checkHook) { SaManager.getLog().info("防火墙校验 hook 注册成功: " + checkHook.getClass()); checkHooks.add(checkHook); diff --git a/sa-token-core/src/main/java/cn/dev33/satoken/strategy/hooks/SaFirewallCheckHookForPathBannedCharacter.java b/sa-token-core/src/main/java/cn/dev33/satoken/strategy/hooks/SaFirewallCheckHookForPathBannedCharacter.java new file mode 100644 index 00000000..dff58190 --- /dev/null +++ b/sa-token-core/src/main/java/cn/dev33/satoken/strategy/hooks/SaFirewallCheckHookForPathBannedCharacter.java @@ -0,0 +1,53 @@ +/* + * Copyright 2020-2099 sa-token.cc + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package cn.dev33.satoken.strategy.hooks; + +import cn.dev33.satoken.context.model.SaRequest; +import cn.dev33.satoken.context.model.SaResponse; +import cn.dev33.satoken.exception.RequestPathInvalidException; +import cn.dev33.satoken.util.SaFoxUtil; + +/** + * 防火墙策略校验钩子函数:请求 path 禁止字符校验 + * + * @author click33 + * @since 1.41.0 + */ +public class SaFirewallCheckHookForPathBannedCharacter implements SaFirewallCheckHook { + + /** + * 默认实例 + */ + public static SaFirewallCheckHookForPathBannedCharacter instance = new SaFirewallCheckHookForPathBannedCharacter(); + + /** + * 执行的方法 + * + * @param req 请求对象 + * @param res 响应对象 + * @param extArg 预留扩展参数 + */ + @Override + public void execute(SaRequest req, SaResponse res, Object extArg) { + // 非可打印 ASCII 字符检查 + String requestPath = req.getRequestPath(); + if(SaFoxUtil.hasNonPrintableASCII(requestPath)) { + throw new RequestPathInvalidException("请求 path 包含禁止字符:" + requestPath, requestPath); + } + + } + +} diff --git a/sa-token-core/src/main/java/cn/dev33/satoken/util/SaFoxUtil.java b/sa-token-core/src/main/java/cn/dev33/satoken/util/SaFoxUtil.java index ab6aef55..a9cf5f62 100644 --- a/sa-token-core/src/main/java/cn/dev33/satoken/util/SaFoxUtil.java +++ b/sa-token-core/src/main/java/cn/dev33/satoken/util/SaFoxUtil.java @@ -809,4 +809,24 @@ public class SaFoxUtil { return listX; } + /** + * 检查字符串是否包含非可打印 ASCII 字符 + * @param str / + * @return / + */ + public static boolean hasNonPrintableASCII(String str) { + if (str == null) { + return false; + } + for (int i = 0; i < str.length(); i++) { + char c = str.charAt(i); + // ASCII 范围检查:0-31 或 127 + if ((c <= 31) || (c == 127)) { + return true; + } + } + return false; + } + + } diff --git a/sa-token-test/sa-token-springboot-test/src/test/java/cn/dev33/satoken/core/util/SaFoxUtilTest.java b/sa-token-test/sa-token-springboot-test/src/test/java/cn/dev33/satoken/core/util/SaFoxUtilTest.java index 81551159..e3e5e14f 100644 --- a/sa-token-test/sa-token-springboot-test/src/test/java/cn/dev33/satoken/core/util/SaFoxUtilTest.java +++ b/sa-token-test/sa-token-springboot-test/src/test/java/cn/dev33/satoken/core/util/SaFoxUtilTest.java @@ -15,6 +15,10 @@ */ package cn.dev33.satoken.core.util; +import cn.dev33.satoken.util.SaFoxUtil; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + import java.time.Instant; import java.time.ZoneId; import java.time.ZonedDateTime; @@ -23,11 +27,6 @@ import java.util.Arrays; import java.util.List; import java.util.Map; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Test; - -import cn.dev33.satoken.util.SaFoxUtil; - /** * SaFoxUtil 工具类测试 * @@ -401,4 +400,12 @@ public class SaFoxUtilTest { Assertions.assertEquals(list.get(2), "c"); } + @Test + public void hasNonPrintableASCII() { + Assertions.assertFalse(SaFoxUtil.hasNonPrintableASCII("Hello World!")); + Assertions.assertTrue(SaFoxUtil.hasNonPrintableASCII("Hello\u0007World")); + Assertions.assertTrue(SaFoxUtil.hasNonPrintableASCII("Hello\tWorld")); + Assertions.assertTrue(SaFoxUtil.hasNonPrintableASCII("Hello\nWorld")); + } + }