【五一创作】SSO单点登录 防篡改、防重放、防盗用

引言

上期我们讲述如何基于ticket+token+redis实现单点登录功能,但是将ticket直接拼接在地址栏中很容易引发安全问题,下面是我的解决方案

项目概况

首先,SSO与子系统之间相互分开,SSO生成全局会话,当SSO系统登出时,子系统也会随之登出,当子系统单独登出时,SSO也会保持原来登录状态(相当于全局会话登出局部会话也会退出,但局部会话登出不会影响全局会话),SSO会子系统颁发一个ticket,由于ticket非常容易被获取,所以安全性显得尤为重要。

安全优化

防篡改

ticket中存放了用于用户登录的用户凭证(如userId),如果被篡改成黑客自己的userId,那么黑客就可以直接登录子系统了。
解决方案 参考JWT的加密原理再结合数字签名,只要secret不被泄露,就无法生成正确的数字签名,从而保障ticket的防篡改

防盗用

当黑客通过请求url获取到其他人的ticket时,上面的安全措施直接被跳过,黑客能够用ticket使用其他人的身份进行登录。那么,我们需要一个用户独特的标识去识别用户的登录行为,在这里,我们采用的是获取用户真实IP地址存放到ticket中,类似于放置userId在ticket中。
校验ticket时,需要再次获取用户ip,并对照两个ip是否一致。
与用户凭证userId相同,黑客依然可以看到里面内容,黑客可以伪造自己的ip为ticket中的ip,这时,我们需要应对IP伪造。
如何应对IP伪造
伪造ip一般是通过伪造 X-Forwarded-For 来实现的,但是客户端请求来源IP其实是不能被伪造的,因为在客户端和服务端进行通信的时候,我们需要进行三次握手,如果这个来源IP是假的,那么我们的握手是不会成功的。
此时我们可以通过nginx来获取到请求的来源IP:

proxy_set_header X-Real-IP $remote_addr

$remote_addr 是 nginx 的内置变量,代表客户端的真实IP。
对于最外层的代理服务器,我们可以进行如下配置:

proxy_set_header X-Forwarded-For $remote_addr

而内部的代理服务器则保留原有的 X-Forwarded-For 配置,这样,我们就可以获得真实的客户端IP了。

防重放

防重放 顾名思义 一个合法请求被非法获取并重复发送该请求,造成服务器资源的浪费,甚至可能搞崩系统,是一种基于资源消耗的DoS攻击
与幂等不同,幂等是允许执行多次,只不过执行多次后的结果依然是一样的 而防重放,是不允许执行多次,从源头上减少对服务器资源的浪费

如何防止重放攻击

主要是让合法的请求,只能被执行一次

思路1 nginx限流配置

我们可以在nginx中结合项目特点通过合理的配置限制连接数和请求数
感兴趣的可以了解一下nginx限流配置

     # 定义限流策略 速率限制  
     # 2r/s,转换一下就是 500ms 内单个 IP 只允许通过 1 个请求,从 501ms 开始才允许通过第 2 个请求。
     limit_req_zone $binary_remote_addr zone=mylimit:10m rate=4r/s;
     # 控制并发数
     limit_conn_zone $binary_remote_addr zone=perip:10m;
     limit_conn_zone $server_name zone=perserver:10m;
# burst=4 表示每个 IP 可以有4 个请求被放到 burst 队列里排队执行了
           # 1s 处理4个请求
           limit_req zone=mylimit burst=4;
          # 一个ip同时最多持有4个连接
          limit_conn perip 4;
          # server 同时能处理并发连接的总数为 40个
          limit_conn perserver 80;

这相当于在请求转发时就已经将请求拦截,不过参数不太好设置,过高过低都可能会影响使用体验

思路2 采用时间戳的方式

在发送请求时携带一个当前的时间戳timestamp,假设从用户发起请求到达服务器不会超过60s,当请求到达服务器时,判断当前服务的时间减去timestamp,若小于60,则认为是合理的,大于60则可能是黑客抓包发起的请求。
为什么要这么做呢?主要是从黑客抓包重放请求耗时一般超过60s,当若黑客修改timestamp,似乎就绕过了后台校验
于是,我们需要跟前端约定好secret,对参数进行MD5加密,后台对收到的请求用同样的方法进行MD5加密,并判断是否被篡改。相当于前面的防篡改过程。
存在问题 虽然我们能有效防止60s内的重放攻击,但规律过于明显,如果黑客在60s再次发起请求的话,那还是防范不了的。于是,我们需要保证每次请求的唯一性。

随机数nonce

nonce是仅使用一次的随机字符串,需要保证每次请求所携带的nonce各不相同。当携带有用过的nonce到来时,我们可以认为该请求是非法的。
但是,会带来很大的内存消耗,而且要保证全局唯一(可以参考雪花算法实现),nonce可以是用户名等用户唯一标识

时间戳+随机数

我们可以将两者的特点结合起来,让时间戳去防止60s外的攻击,nonce只存在60s,用来防止60s内的重放攻击。
于是,我们的流程可以是

携带加密的时间戳
小于60s
大于60s
客户端发起请求
客户端验证
检验是否存在nonce
不存在则请求合法
非法请求

当请求合法时,将nonce存进redis,并设置1min后过期,用于防止60s内的攻击

总结

主要是对单点登录系统中ticket进行防篡改、防重放、防盗用的安全优化,虽然在一般的系统中其实也用不到这较为完善的方案(学校项目会进行安全检测),但也能有效提高开发人员安全意识和技能,本次专栏到此就结束了,欢迎大家补充。

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值