写在前面

也是相当久没写博客了,最近兴起写一下吧。

为什么我选择写AuthBridge

这个也算是非常旧的事情了,我们很早之前就想将频道接入到江财统一认证,保证频道不会出现过多外来人员的干扰,让频道变得更像一个校园论坛?(我是这么看的)
然后作为常年咕咕的狐,我也是在今年选择把这个多年未开工的项目写出来。于是乎在今年开学就开工了这个项目。

架构选择

作为一个仅仅作为认证桥接的小工具,我决定不应该使用过于复杂的组件,并且我之前是写过一个基于PHP的laravel的一个项目的插件。我对SSR(服务器渲染)方式带有极大的好感,并且看过VUE实现的客户端渲染的最终HTML码,我觉得我自己不太喜欢这种不太可控的感觉。所以我先选择了Express框架作为项目基础,再使用Ejs作为模板组件。
选择完语言基础就要开始决定怎么一个流程,作为一个初出茅庐的程序员,我本身更偏向极致的安全和隐私。所以我上来就决定连我自己都难以还原学号的方式,最开始我打算使用hash,甚至是加盐bcrypt或者Argon2这种慢哈希。但是我很快意识到一个问题,学号空间过小。理论上江财学号11位数,应该有10^11这么大的空间,但是很快你就会发现,很多空间是根本不存在的,实际的空间就是在校生人数。假如你能掌握学号的分布的话,这个空间就会瞬间从10^11压缩到不足百万,这个在密码学中是一个很头疼的事情。我最开始的假设是能否在密钥、盐、数据库等一切都泄露的情况下,能否仍旧保证学生学号还是绝对的隐私。但是这个明文空间摆在这里,一个彩虹表就可以把我干趴下。
我曾经想过使用别的信息作为额外的私有盐,或者说通过额外信息增加熵,但是我很快发现这是极其不负责的行为,因为无论使用姓名还是电话号码等,我就等于获取了更多不必要信息,增加了泄露的风险。
于是乎哪怕用慢哈希也难以救回,我只能寄情于保证盐或者密钥仍旧是隐秘的状态下,学生学号信息是安全的。所以这时我决定使用bcrypt作为摘要方式,并且把cost和盐长度拉到足够大,并且我要求后端应用再次加盐hash,以达到一个服务器被击破,后端应用数据库还是安全的状态。
后来我很快意识到,这种私密似乎是不可取的,审计是个很大的问题,我不能将学号这种唯一身份信息用复杂方式去除然后生成独一无二的身份标识,并且还不能还原。再加上首先当信息熵足够小的时候,我性能和破解者性能差距足够大的时候,argon2的开销就已经很难满足我的需求了,再加上审计需求,最后才彻底放弃摘要。所以最后还是选择加密。那就开始在RSA、AES、ECC等里面选。最终选择ECC加AES-gcm。
自此整个架构选择就明了了,CAS登录,临时用Redis存储CAS Session,生成JWT,销毁Session。因为Session交给了Express-session库,内部细节就不需要我过多考虑了。
因为时间已经过去有一段时间了,很多细节我都遗忘了我为什么这样,但是我有我自己的理由。

开发

开发我不可能完整讲流程,反正是大致将文件夹先拆分开,将加密类共享放在utils组件文件夹,然services主要处理业务,还有一个专门用于密钥生成等一次性与核心链路无关工作的scripts文件夹等等。
开发流程差不多半个月多,陆陆续续改架构,重构,引入CI/CD,修bug,补进去i18n等等。具体细节我还是更建议看commits。
整体在我看来已经算是比较稳定了,该捕获的捕获了,该落盘写日志的写了,该对前端隐藏的隐藏了。最后效果还算可以,整体响应的瓶颈完全在CAS侧。
在我看来,我对这次开发的过程和结果都算是比较满意的,虽然不可避免出现瑕疵,但是我觉得,风险在相对可控的范围。
后续为了更加适配场景,我极大压缩了jwt的携带信息,包括但不限于签发者,和接受者,还有key生成时间,以及唯一标识(iss、aud、iat、jti)。因为这些分别由:独立的签名密钥,立即签发,和aes-gcm和ecc的随机向量解决了。
最后当初选择的SSR也带来了极大的好处,1.显示完全由拼接和服务端输出决定,结果相当可控。2.显示速度极快。3.发送时只有少量文件,甚至非html都是30min缓存。并且开发上也相当简易和清晰。
对于XSS防护和CSRF防护,我都做了专门的处理,比如禁止大部分脚本,只允许少量同源脚本,用cookie做了一个简单的csrf防护。
为了开发我还在测试机上单独搭建了一个CAS服务,然后在本地通过ssh隧道将本地端口穿透到测试机网络,然后就能用测试机完成初步测试,最后上传git触发CI/CD,完成更新。

最后

这个项目我还是会随着我的见识和代码量的上升慢慢更新掉一些我之前没认识到的问题,但更多是安全性更新,我觉得这种桥接工具,我已经把链路做的足够简单了。算是一个LTS公告吧,但是这个LTS更新频率和截止时间就不好说了。