mirror of
https://gitee.com/dromara/sa-token.git
synced 2025-05-04 12:47:55 +08:00
禁止 allow-url 配置项 * 符号出现在中间位置,因为这有可能导致校验被绕过
This commit is contained in:
parent
f35a6f6ad8
commit
c169721ebf
@ -22,10 +22,12 @@ import cn.dev33.satoken.sso.function.CheckTicketAppendDataFunction;
|
|||||||
import cn.dev33.satoken.sso.function.DoLoginHandleFunction;
|
import cn.dev33.satoken.sso.function.DoLoginHandleFunction;
|
||||||
import cn.dev33.satoken.sso.function.NotLoginViewFunction;
|
import cn.dev33.satoken.sso.function.NotLoginViewFunction;
|
||||||
import cn.dev33.satoken.sso.function.SendHttpFunction;
|
import cn.dev33.satoken.sso.function.SendHttpFunction;
|
||||||
|
import cn.dev33.satoken.sso.template.SaSsoServerTemplate;
|
||||||
import cn.dev33.satoken.util.SaFoxUtil;
|
import cn.dev33.satoken.util.SaFoxUtil;
|
||||||
import cn.dev33.satoken.util.SaResult;
|
import cn.dev33.satoken.util.SaResult;
|
||||||
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sa-Token SSO 单点登录模块 配置类 (Server端)
|
* Sa-Token SSO 单点登录模块 配置类 (Server端)
|
||||||
@ -134,6 +136,11 @@ public class SaSsoServerConfig implements Serializable {
|
|||||||
* @return 对象自身
|
* @return 对象自身
|
||||||
*/
|
*/
|
||||||
public SaSsoServerConfig setAllowUrl(String allowUrl) {
|
public SaSsoServerConfig setAllowUrl(String allowUrl) {
|
||||||
|
// 提前校验一下配置的 allowUrl 是否合法,让开发者尽早发现错误
|
||||||
|
if(SaFoxUtil.isNotEmpty(allowUrl)) {
|
||||||
|
List<String> allowUrlList = SaFoxUtil.convertStringToList(allowUrl);
|
||||||
|
SaSsoServerTemplate.checkAllowUrlListStaticMethod(allowUrlList);
|
||||||
|
}
|
||||||
this.allowUrl = allowUrl;
|
this.allowUrl = allowUrl;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
@ -243,7 +250,7 @@ public class SaSsoServerConfig implements Serializable {
|
|||||||
* @return 对象自身
|
* @return 对象自身
|
||||||
*/
|
*/
|
||||||
public SaSsoServerConfig setAllow(String ...url) {
|
public SaSsoServerConfig setAllow(String ...url) {
|
||||||
this.allowUrl = SaFoxUtil.arrayJoin(url);
|
this.setAllowUrl(SaFoxUtil.arrayJoin(url));
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -65,4 +65,7 @@ public interface SaSsoErrorCode {
|
|||||||
/** 在 /sso/auth 既没有指定 redirect 参数,也没有配置 homeRoute 路由 */
|
/** 在 /sso/auth 既没有指定 redirect 参数,也没有配置 homeRoute 路由 */
|
||||||
int CODE_30014 = 30014;
|
int CODE_30014 = 30014;
|
||||||
|
|
||||||
|
/** 无效的 allow-url 配置 */
|
||||||
|
int CODE_30015 = 30015;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -264,32 +264,82 @@ public class SaSsoServerTemplate extends SaSsoTemplate {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 3、不允许出现@字符
|
// 3、不允许出现@字符
|
||||||
// 为什么不允许出现 @ 字符呢,因为这有可能导致 redirect 参数绕过 AllowUrl 列表的校验
|
|
||||||
// 举个例子 配置文件:
|
|
||||||
// sa-token.sso-server.allow-url=http://sa-sso-client1.com*
|
|
||||||
// 开发者原意是为了允许 sa-sso-client1.com 下的所有地址都可以下放ticket
|
|
||||||
// 但是如果攻击者精心构建一个url:
|
|
||||||
// http://sa-sso-server.com:9000/sso/auth?redirect=http://sa-sso-client1.com@sa-token.cc
|
|
||||||
// 那么这个url就会绕过 allow-url 的校验,ticket 被下发到了第三方服务器地址:
|
|
||||||
// https://sa-token.cc/?ticket=i8vDfbpqBViMe01QoLY1kHROJWYvv9plBtvTZ6kk77KK0e0U4Xj99NPfSZEYjRul
|
|
||||||
// 造成了ticket 参数劫持
|
|
||||||
// 所以此处需要禁止在 url 中出现 @ 字符
|
|
||||||
// 这么一刀切的做法,可能会导致一些特殊的正常url也无法通过校验,例如:
|
|
||||||
// http://sa-sso-server.com:9000/sso/auth?redirect=http://sa-sso-client1.com:9003/@getInfo
|
|
||||||
// 但是为了安全起见,这么做还是有必要的
|
|
||||||
if(url.contains("@")) {
|
if(url.contains("@")) {
|
||||||
|
// 为什么不允许出现 @ 字符呢,因为这有可能导致 redirect 参数绕过 AllowUrl 列表的校验
|
||||||
|
//
|
||||||
|
// 举个例子 配置文件:
|
||||||
|
// sa-token.sso-server.allow-url=http://sa-sso-client1.com*
|
||||||
|
//
|
||||||
|
// 开发者原意是为了允许 sa-sso-client1.com 下的所有地址都可以下放ticket
|
||||||
|
//
|
||||||
|
// 但是如果攻击者精心构建一个url:
|
||||||
|
// http://sa-sso-server.com:9000/sso/auth?redirect=http://sa-sso-client1.com@sa-token.cc
|
||||||
|
//
|
||||||
|
// 那么这个url就会绕过 allow-url 的校验,ticket 被下发到了第三方服务器地址:
|
||||||
|
// https://sa-token.cc/?ticket=i8vDfbpqBViMe01QoLY1kHROJWYvv9plBtvTZ6kk77KK0e0U4Xj99NPfSZEYjRul
|
||||||
|
//
|
||||||
|
// 造成了ticket 参数劫持
|
||||||
|
// 所以此处需要禁止在 url 中出现 @ 字符
|
||||||
|
//
|
||||||
|
// 这么一刀切的做法,可能会导致一些特殊的正常url也无法通过校验,例如:
|
||||||
|
// http://sa-sso-server.com:9000/sso/auth?redirect=http://sa-sso-client1.com:9003/@getInfo
|
||||||
|
//
|
||||||
|
// 但是为了安全起见,这么做还是有必要的
|
||||||
throw new SaSsoException("无效redirect(不允许出现@字符):" + url).setCode(SaSsoErrorCode.CODE_30001);
|
throw new SaSsoException("无效redirect(不允许出现@字符):" + url).setCode(SaSsoErrorCode.CODE_30001);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 4、判断是否在[允许地址列表]之中
|
// 4、判断是否在 [ 允许的地址列表 ] 之中
|
||||||
List<String> authUrlList = Arrays.asList(getAllowUrl().replaceAll(" ", "").split(","));
|
List<String> allowUrlList = Arrays.asList(getAllowUrl().replaceAll(" ", "").split(","));
|
||||||
if( ! SaStrategy.instance.hasElement.apply(authUrlList, url) ) {
|
checkAllowUrlList(allowUrlList);
|
||||||
|
if( ! SaStrategy.instance.hasElement.apply(allowUrlList, url) ) {
|
||||||
throw new SaSsoException("非法redirect:" + url).setCode(SaSsoErrorCode.CODE_30002);
|
throw new SaSsoException("非法redirect:" + url).setCode(SaSsoErrorCode.CODE_30002);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 校验通过 √
|
// 校验通过 √
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 校验配置的 AllowUrl 是否合规,如果不合规则抛出异常
|
||||||
|
* @param allowUrlList 待校验的 allow-url 地址列表
|
||||||
|
*/
|
||||||
|
public void checkAllowUrlList(List<String> allowUrlList){
|
||||||
|
checkAllowUrlListStaticMethod(allowUrlList);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 校验配置的 AllowUrl 是否合规,如果不合规则抛出异常
|
||||||
|
* @param allowUrlList 待校验的 allow-url 地址列表
|
||||||
|
*/
|
||||||
|
public static void checkAllowUrlListStaticMethod(List<String> allowUrlList){
|
||||||
|
for (String url : allowUrlList) {
|
||||||
|
int index = url.indexOf("*");
|
||||||
|
// 如果配置了 * 字符,则必须出现在最后一位,否则属于无效配置项
|
||||||
|
if(index != -1 && index != url.length() - 1) {
|
||||||
|
// 为什么不允许 * 字符出现在中间位置呢,因为这有可能导致 redirect 参数绕过 allow-url 列表的校验
|
||||||
|
//
|
||||||
|
// 举个例子 配置文件:
|
||||||
|
// sa-token.sso-server.allow-url=http://*.sa-sso-client1.com
|
||||||
|
//
|
||||||
|
// 开发者原意是为了允许 sa-sso-client1.com 下的所有子域名都可以下放ticket
|
||||||
|
// 例如:http://shop.sa-sso-client1.com
|
||||||
|
//
|
||||||
|
// 但是如果攻击者精心构建一个url:
|
||||||
|
// http://sa-sso-server.com:9000/sso/auth?redirect=http://sa-token.cc/a.sa-sso-client1.com/sso/login
|
||||||
|
//
|
||||||
|
// 那么这个 url 就会绕过 allow-url 的校验,ticket 被下发到了第三方服务器地址:
|
||||||
|
// https://sa-token.cc/a.sa-sso-client1.com/sso/login?ticket=v2KKMUFK7dDsMMzXLQ3aWGsyGUjrA0dBB2jeOWrpCnC8b5ScmXXQSv20mIwPK7Cx
|
||||||
|
//
|
||||||
|
// 造成了 ticket 参数劫持
|
||||||
|
// 所以此处需要禁止 allow-url 配置项的中间位置出现 * 字符(出现在末尾是没有问题的)
|
||||||
|
//
|
||||||
|
// 这么一刀切的做法,可能会导致正常场景下的子域名url也无法通过校验,例如:
|
||||||
|
// http://sa-sso-server.com:9000/sso/auth?redirect=http://shop.sa-sso-client1.com/sso/login
|
||||||
|
//
|
||||||
|
// 但是为了安全起见,这么做还是有必要的
|
||||||
|
throw new SaSsoException("无效的 allow-url 配置(*通配符只允许出现在最后一位):" + url).setCode(SaSsoErrorCode.CODE_30015);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// ------------------- SSO -------------------
|
// ------------------- SSO -------------------
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user