{"record":{"id":"8f46cd58ed16f3a9","repo":"dromara/Sa-Token","slug":"code-30015","errorCode":"CODE_30015","errorMessage":"无效的 allow-url 配置（*通配符只允许出现在最后一位）：{url}","messagePattern":"无效的 allow-url 配置（\\*通配符只允许出现在最后一位）：(.+?)","errorType":"exception","errorClass":"SaSsoException","httpStatus":null,"severity":"error","filePath":"sa-token-plugin/sa-token-sso/src/main/java/cn/dev33/satoken/sso/template/SaSsoServerTemplate.java","lineNumber":499,"sourceCode":"                //      sa-token.sso-server.allow-url=http://*.sa-sso-client1.com\n                //\n                //  开发者原意是为了允许 sa-sso-client1.com 下的所有子域名都可以下放ticket\n                //      例如：http://shop.sa-sso-client1.com\n                //\n                //  但是如果攻击者精心构建一个url：\n                //       http://sa-sso-server.com:9000/sso/auth?redirect=http://sa-token.com/a.sa-sso-client1.com/sso/login\n                //\n                //  那么这个 url 就会绕过 allow-url 的校验，ticket 被下发到了第三方服务器地址：\n                //       http://sa-token.com/a.sa-sso-client1.com/sso/login?ticket=v2KKMUFK7dDsMMzXLQ3aWGsyGUjrA0dBB2jeOWrpCnC8b5ScmXXQSv20mIwPK7Cx\n                //\n                //  造成了 ticket 参数劫持\n                //  所以此处需要禁止 allow-url 配置项的中间位置出现 * 字符（出现在末尾是没有问题的）\n                //\n                //  这么一刀切的做法，可能会导致正常场景下的子域名url也无法通过校验，例如：\n                //       http://sa-sso-server.com:9000/sso/auth?redirect=http://shop.sa-sso-client1.com/sso/login\n                //\n                //  但是为了安全起见，这么做还是有必要的\n                throw new SaSsoException(\"无效的 allow-url 配置（*通配符只允许出现在最后一位）：\" + url).setCode(SaSsoErrorCode.CODE_30015);\n            }\n        }\n    }\n\n\n    // ------------------- 单点注销 -------------------\n\n    /**\n     * 为指定账号 id 注册应用接入信息（模式三）\n     *\n     * @param loginId 账号id\n     * @param client 指定客户端标识，可为null\n     * @param sloCallbackUrl 单点注销时的回调URL\n     */\n    public void registerSloCallbackUrl(Object loginId, String client, String sloCallbackUrl) {\n        // 如果提供的参数是空值，则直接返回，不进行任何操作\n        if(SaFoxUtil.isEmpty(loginId)) {\n            return;","sourceCodeStart":481,"sourceCodeEnd":517,"githubUrl":"https://github.com/dromara/Sa-Token/blob/ac2c7f6e94a78573cf0bcb932dd8b04e68fad189/sa-token-plugin/sa-token-sso/src/main/java/cn/dev33/satoken/sso/template/SaSsoServerTemplate.java#L481-L517","documentation":"Thrown by checkAllowUrlList at server startup or on first use: an entry in the client's allow-url config has a '*' wildcard somewhere other than the last character. The code comment explains why — a mid-string '*' (e.g. http://*.client1.com/sso/login) lets attacker URLs like http://sa-token.com/a.client1.com/sso/login pass suffix/contains matching, causing ticket hijacking.","triggerScenarios":"Configuring allow-url: http://*.example.com/sso/login (leading or middle '*') on the sso-server; the check iterates every entry and rejects the offending one.","commonSituations":"Admin tries to whitelist all subdomains with a leading-star pattern and hits this deliberate restriction; migrating from an older sa-token that did not validate this.","solutions":["Use the wildcard only as a trailing suffix, e.g. http://example.com/* — subdomain-level wildcards are intentionally not supported","List each concrete subdomain explicitly: http://shop.example.com/sso/login,http://api.example.com/sso/login","If many subdomains are needed, front them with one shared callback host and route internally"],"exampleFix":"# before\nallow-url: http://*.example.com/sso/login   # CODE_30015\n\n# after\nallow-url: http://shop.example.com/sso/login,http://api.example.com/sso/login","handlingStrategy":"validation","validationCode":"for(String u : allowUrlList) {\n    int i = u.indexOf('*');\n    if(i != -1 && i != u.length() - 1) {\n        // fix config before server starts rejecting it\n    }\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Validate allow-url entries in CI with a config lint step","Remember: wildcards are suffix-only by design; enumerate subdomains explicitly"],"tags":["sa-token","sso","config","security","wildcard"],"backgroundTag":null,"analyzedSha":"ac2c7f6e94a78573cf0bcb932dd8b04e68fad189","analyzedAt":"2026-08-14T14:36:10.271Z","schemaVersion":2},"datasetVersion":"2026-08-16T03:17:38.424Z"}