对楼上几个回答做个简单补充:
注:下面提到的安全性都是假设 CAS 服务是安全的,而下面单个 App 可能存在安全漏洞。而降低安全维护成本是指,你只需要重点考虑 CAS 的安全性即可将风险降到最低,漏洞影响不至于扩散到所有业务(但并不是说下属 App 的漏洞就不需要补了)
@
ZSeptember #3 没有提到从 CAS 带回 App 的 cookie 应当如何验证,存在两个问题:
1. 从安全的角度来说,不应当携带 CAS 的 cookie 回到 App ,因为这个 cookie 一旦泄漏,影响将会直接扩散到整个 CAS 下接入的所有服务,一个 cookie 就可以走遍所有服务了。这增大了安全维护成本,任何一个 App 存在安全漏洞,都会扩大影响到所有其他 App 。
2. 每个接入的 App 都要对 CAS 的 cookie 信息做兼容,App 后台需要同步存储对应的 cookie key 信息,或者统一向 CAS 请求验证增大单点服务的带宽压力。而如果采用类似 JWT 的方式, 这种方式不仅存在天生的设计缺陷(不支持注销强制过期、续期需要跳回 CAS 来进行等),还需要在每个接入的 App 后台存储 secret key ,和上面一条类似,增大了安全维护成本,一个 App 存在漏洞泄漏了 key ,会直接影响所有 App ;并且要更新 key 也需要统一通知所有接入的 App 进行升级,这个成本太大。
@
InDom #7 提到了 cookie 的安全性问题,并做了改进。但是还是存在一些问题:
1. 采用将用户名加密或签名带过去的方式,也存在问题,同样也是安全维护成本的问题。如果一个 App 存在安全漏洞,那么当用户登录这个 App 的时候,攻击者就可以立即获取带过来的加密或签名过的用户名,使用这个数据就可以立即解锁所有 App (因为的确是 CAS 进行的加密/签名操作,所以肯定是合法的)。
2. 使用 JWT (就是做签名)意义不大,即便 JWT 存在过期机制(就是同时签名了个时间戳),但攻击者通常都是使用脚本进行攻击的,拿到 JWT 之后瞬间就可以解锁所有服务,JWT 过期时间定多久都不合适。
3. 使用 Ajax 不可取。CAS 与 App 的域名肯定不同,这就属于跨域请求了,那么 CAS 的登录 cookie 就属于第三方 cookie 了。目前浏览器在逐步将 cookie 的 SameSite 属性默认值改为 Lax ,也就是不发送第三方 cookie ,那么 CAS 在 Ajax 的情况下永远都是返回未登录。除非你已经部署 https 并且主动在 CAS 上将 cookie 的 SameSite 属性设置为 None ,但这样就会引发跨站攻击。因为 CAS 要允许 App 跨域登录,必然要设置放行所有域名的 CORS 头,那么任何第三方网站就都可以发起对 CAS 的跨站攻击了。除非 CAS 预知所有 App 的域名,做 CORS 拦截,但这样就损失了业务扩展的便利性,新 App 必须提前向 CAS 注册。
#8 的描述是比较全面的,CAS 跳回 App 时带一个一次性的有时效的 Token ,App 后台向 CAS 查询这个 Token 的有效性并得到用户信息,此时 Token 失效。单个 App 存在漏洞也不会扩散影响到其他 App ,因为 Token 是一次性的,在有漏洞的 App 中用掉了就不能用在其他 App 中了。
前面两个方案的问题都是出在 CAS 返回的 Token 的有效次数上,在 Token 的有效期内可以任意次用于解锁任意服务,没有办法保证 Token 在使用后立即丢弃。第二个方案可以在加密/签名的信息中增加 App 名称做限制,但也存在伪造的可能性,因为 CAS 和 App 互相不知道提供的 App 名称的真实性,可能是由用户客户端的某个 XSS 脚本提供的。
而第三个方案则是补上了这些漏洞,由 CAS 和 App 互相在后台再做一次互认来实现。