前几天老妈办退休,需要到社保网站上打印个人参保证明。由于老妈对互联网这块儿的认知只有手机上的今日头条和拼多多,如此重任自然就落到了身为码农的我的头上。
访问社保公共服务平台,用老妈常用的登录密码一顿操作,发现都登录不上。用网站提供的扫码登录,也会报错。
不能是老妈当初灵感迸发,随便想了个新密码吧?试了一下密码找回,提示 “未找到有效账户”。这就基本实锤,我需要先注册一波。
进入个人注册页面,轻车熟路填好信息,满怀信心点击下一步,一个报错给我弹傻了:
结合填写的信息(姓名、手机、身份证),怀疑可能是手机号不在老妈名下导致的。由于我临时有事要出门,交待老妈抽空到楼下营业厅办张卡,晚上我回来再试试。
晚上回家卡已经办好了,重走一波流程,居然还是上午那个报错……
这就迷了,信息我反复确认了几次,甚至让媳妇儿帮忙敲了一遍,肯定没错,为啥就提示不匹配?职业习惯提醒我:这不能是个 bug 吧??
本着跟 Bug 死磕到底的信念,习惯性的祭出了 Chrome 的 F12,想看看到底是什么神仙逻辑(由于是地方 zf 的网站,经验上感觉逻辑会有挺多漏洞,万一是个纯前端校验,那我绕过就好了)。
切到 Network,重新提交表单,直接定位到关键请求:
直接 Initiator 里看下请求发出的位置,发现业务逻辑都在内联脚本里……
回调逻辑很简单,就是判断下返回的 code:
1609 行打个断点,重新提交表单,触发断点后,在 console 里直接改写data.fieldData.code
的值,继续执行,就顺利跳到下一步了……一不小心就完成了注册……
由于走了野路子,担心影响老妈后续的流程,特意给社保打了电话,确认信息都是正常的……
如果我们假定核心接口是没有 Bug 的,即老妈的身份校验就应该是不通过,从前端的角度来看,网站方面其实有不少问题的。这些问题在我之前的项目测试中也遇到过,也都跟组里强调过,结合这次经验总结分享一下。
Web 技术的一大特点,就是明文。结合图四可以看到,判断逻辑非常清晰。作为开发和测试,我们是非常喜欢这种代码的;但是面对用户,尤其是别有用心的用户,逻辑应该是隐藏起来的,应该越难懂越好。
关于 JS 的加密和混淆,有很多现成的方案,不一一列举了。
我们看图四的 checkRelInfo 接口的请求体,提交的内容都是加密的:
这种加密不能说没有用,它更多的是防止劫持或监听导致的信息泄漏。对业务安全本身的影响不大。
数据加密一般最好配合 HTTPS 使用,效果最佳。如果都是 HTTP 传输,其实可以通过篡改 HTML 或者 JS 库实现加密前的数据拦截与上报,使加密算法形同虚设。
可以看到,checkRelInfo 接口是对用户输入进行的最后一次校验,后续的信息提交是默认用户已经通过了校验的。这其实是一个比较常见的问题,也需要结合具体业务具体分析才能发现。后端时常会说一句话:只有通过流程 A,才能调到接口 B,所以 B 没必要做校验。具体会不会有这种情况,除了结合产品逻辑外,也要把用户当成专业的坏人:直接抓包再请求 B,就可以绕过所有的业务限制了啊。
几年前媳妇儿报名会计考试,死活报名失败,当时也是通过断点定位原因、改代码帮着解了一个 Bug 才报上的名……
从体验层面上看,除去 Bug 本身,没有反馈渠道其实更头疼,毕竟不是所有人都能想办法绕过各种问题,也不是所有问题都能从前端绕过。
相比质量提高,良好的反馈/客服机制其实也很重要。作为测试,再怎么搞也不能发现所有问题,但是我们期望所有 Bug 的影响都能降到最低。