安全的身份验证系统的设计
公司大了,内网系统多了,关键性信息和商业秘密也多了以后,内网信息安全就成了很重要的命题,使用各种框架默认带有的类库,直接弄一个简单的用户名密码校验就已经不满足需求了。
设计一个身份验证解决方案就势在必行,当然了,设计一个解决方案,不一定非要自己撸代码,可以对接、购买,关键是看你的需求是什么。当然,我们还是选择了自己实现一个,这可能不是一个很好的选择,要在此声明这一点。
为什么要自己做?
自己实现有很多的原因了,比如三方的帐号不够安全(腹诽:其实微信的帐号身份验证已经足够安全了,相比大多数自己撸的系统来说,一个被无数人动脑子攻击的系统,其帐号验证的安全性经得起挑战),又比如,三方帐号不够让人信任,假设,你接入了微信,但是微信把你的账密给到一个攻击者,(腹诽:一般不可能,谁能做到这一点,大家心里有数),还比如,在实施三方帐号对接的时候,出现了疏忽和不可避免的错误,这倒是极其可能,比如你用了一个不靠谱的类库代码,这个代码在网上开源广为人知,会导致在衔接处被攻击,而且攻击很有针对性。
不过,列举了这么多自己做的理由,我可是没有自信自己实现一个,就能规避上面的全部问题,比如你的研发管理是否足够规范和安全?来自内部的攻击是否能够被极大的防范?君不见删库跑路的刚刚判刑。可能绝大多数公司的管理水平,这一点都保证不了,你的完美设计,基本都建立在沙滩上。你的设计是否全面完美,也是值得被挑战的地方。
面临些什么问题?
说了那么多,还是不得不自己做了,有些“你懂的”原因,我就不解释了,硬着头皮也得做,这不算牢骚,能自己设计系统,还是蛮高兴的,主要是反思一下是否“与人谋而不忠乎?”。
SSO
首先,公司肯定不止一个系统,很多的系统,肯定也不止一个开发团队,各做各的业务,你第一个就必须考虑到接入的问题,一定是 SSO 了,不然的话,每个团队各自做身份验证和授权,这么一扩散,你的安全就不好管控了。
那么 SSO 本身就带来很多的问题,用哪种协议呢?OAuth,SAML等等吧(其实我只了解过这俩,但是真的有很多)最简单易用的可能是 OAuth,这也是个个人判断,因为我用的 PHP 真的很多类库的。不过,就我所知,OAuth 其实在安全专家眼里是脆弱不堪的东西,将绝大多数安全的宝压在了 SSL 上面,一个心脏滴血就可以搞死一片。更不要提 OAuth 的实施,一般都很不规范,极容易弄出问题来。
2FA
因为,想弄这个 OAuth,毕竟省力,但是不够安全,就想到了 2FA,再给你叠加一道,应该就好点了吧,比如使用 Google Authenticator,或者使用企业微信钉钉的消息推送,发一个 OTP,当然,用短信发送也行,就是有点短信成本而已。
不过,验证码这种,真的很烦人的,我们选用了一种讨巧的方法,就是客户端证书验证。给每个需要访问内部系统的客户端,颁发一个客户端证书,安装了证书后,在访问内部系统的时候,必须首先向服务器出示,然后再提供密码才能进入。
在公司发展的早期,我们使用运维手动分配客户端证书的方式,反正人少,入职也少,无所谓,后来人越来越多,就不得不自动化这个过程。那么到底使用怎样的方式去分发这个证书,需要非常小心的思考,比如,一个很蠢的设计是,用户先用密码登录证书系统,然后申请证书,就直接发放,这么一来,其实你只要攻破了一个员工的密码,等于证书也可以轻松拿到了,那么还双因素个屁啊?
登录态管理
使用 SSO 后,你有个麻烦就是登录态管理的问题。比如,你要登录 A 系统,你肯定要先登录通行证,然后跳入 A 系统,这没什么问题,但是 A 系统退出的时候,是否要退出通行证呢?问题就来了。
如果在一个可信的电脑上,这是个无所谓的问题,最好不要退出,这样,你登录别的系统,跳通行证的时候,不用反复输入密码,然后授权就可以跳入了。
但是在一个不可信的电脑上,比如网吧里,如果你退出某个业务系统的时候,忘记退出通行证了。那么你的登录态留在了不可信设备上,被冒用的概率是很高的,至少暴露了攻击点出去。
关键原理设计
考虑到上面这些个问题,我们确立了一些比较关键的原理性的设计。
统一的帐号管理,但是只负责身份的验证,不负责权限管理,各个业务系统管理各自的业务权限。也比较符合事实。帐号管理部分,只能管理帐号的基本信息,名字邮箱手机等信息。
各个业务都要使用 SSO 登录,使用最简单的 OAuth 协议来对接。但是,大量敏感系统,必须至于企业内网,统一网关的保护之下,不允许在外网进行访问。对于外网可以进行访问的系统,如无例外,必须校验客户端证书。必须部署在统一网关的后面,就是便于统一的证书验证的部署,避免部署出现疏漏,收敛到少数安全运维的手里。减少部署环节的风险。
证书作为一般来说的第二个因素,实现双因素验证,这样不必太过麻烦,每次输入 OTP 之类的事情。但是证书的颁发,不能仅凭用户密码就颁发,证书的颁发应该来自密码之外的另一个因素。我们想到的是,使用员工预留手机的验证码来申请证书,但是预留手机号如何变更呢?不能是使用用户名密码简单变更。一个简单的处理是,必须使用原预留手机的验证码,才能更换新手机号码。如果,原手机遗失不可用,则不允许更换,只允许立刻冻结,无法走验证更换。必须通过管理员当面审核后手动设置新手机。
那么可以用邮箱来收取 OTP 么?其实并无不可,但是考虑到一般人的差劲的习惯,极有可能把工作邮箱的密码和 OA 系统的密码设为同一个,那等于又是形同虚设了,邮箱我们用的 GMail 的服务,不能检验员工是否使用了相同的密码,所以,就无法信任邮箱的安全性了,员工 OA 密码的泄漏在很大概率上邮箱也泄漏了。
另外,可以考虑其他一些 OTP 方案,比如 Google Authenticator 的绑定,那么绑定的过程,也必须是用另一个因素来验证过身份。以此类推。
一些迷思
我就想到了,比如一些孤立系统,比如 Twitter,GMail 也好,他们的双因素是怎么实现的呢?因为用户首次注册的时候,系统只知道用户名密码,怎么实现第二个因素的绑定呢?我现在猜测,这是一个安全升级的单向过程。
如果你原来只有用户名密码,那么你申请绑定手机,这时候只能信任你,但是你一旦绑定了,就有了第二个因素了,这时候,你想换绑,就不能只验证密码了,这取决于系统对手机这个因素的重要性认证。总之意思就是,你可以提高你帐号的安全级别,但是一旦提高了,就没法降级了(我没验证过这一点)。所以一个空帐号的安全等级很低,随着更多的信息被绑定,因素之间出现互保,你总是要取得两个因素才能攻破。单向升级,互相钳制,就是这么一个过程。
如果一个已经被提高了等级的帐号,你想用单个因素攻破就不可能了。因为单个因素不能给一个帐号的安全降级。我们自己的系统设计,就没有这个冷启动的问题,因为在企业的内部,我们可以直接就通过当面验证的方式把安全等级拉倒高级位置。然后一直锁定在这个位置。
帐号生命周期管理
帐号是怎么产生和回收的呢?因为我们有 OA 系统,必须通过 HR 当面验证,走入职流程来产生内网帐号,通过离职流程来注销帐号,也可以通过紧急报备来冻结特定帐号,或者解冻某个帐号。
不是总结的总结
目前想到了这些东西,我就先记录一下,分享给大家,当然真的做过这种系统的人,可能一眼看出来漏洞百出,还望不吝赐教,写出来也就是跟大家交流和学习的。谢谢了。