发现应用程序的密码安全漏洞
上周三凌晨两点,我盯着一个登录接口的应用响应包,里面明文躺着用户刚设置的程序密码。这不是密洞什么老旧系统——它是一家刚拿到B轮融资的SaaS产品,后台用着最新的码安React和Node.js。问题出在“修改密码”这个功能上,全漏前端把新密码直接塞进URL查询参数,发现后端日志完整记录,应用而运维用的程序日志分析平台默认对所有员工开放。
这种漏洞比SQL注入更隐蔽。密洞SQL注入你能用扫描器跑出来,码安但密码处理不当往往藏在业务逻辑的全漏褶皱里。我见过最离谱的发现一次:某金融App的“忘记密码”流程,验证码校验通过后,应用服务端返回一个JWT,程序里面居然包含用户的旧密码哈希。开发者的解释是“方便前端做二次校验”。
日志里明文记录新密码,这已经够糟了。更麻烦的是,这个日志被Filebeat收集到Elasticsearch,而Kibana的访问权限只区分了“管理员”和“普通成员”。普通成员能搜索所有索引,包括这个叫app-debug-*的日志流。我试着用普通账号登录Kibana,输入new_pwd,三秒后屏幕上整整齐齐列出了最近七天所有用户修改后的密码。
“我们以为只有运维能看日志。”——事后复盘会上,后端负责人这么说。但权限配置是开发环境遗留的,上线时没人改。
密码安全漏洞的根子往往不在加密算法。bcrypt、argon2这些库用对了就没问题。真正出事的是数据在“流动”过程中被暴露:前端日志、网络中间件、APM工具、错误追踪平台(比如Sentry会把请求体完整上报)、甚至客服系统的截图。我见过一个团队用Datadog的RUM采集用户行为,结果把输入框的value属性也带上了,密码在Datadog后台一览无余。
怎么发现这类漏洞?别只依赖SAST工具。手动走一遍所有涉及密码的接口,打开浏览器开发者工具的Network面板,看请求体、URL参数、响应头。然后去服务端翻日志配置文件,确认log4j或winston有没有对password、pwd、secret这些字段做脱敏。有个小技巧:在测试环境故意输入一个带特殊字符的密码,比如P@ssw0rd!<>',然后全局搜索日志文件,看它出现在哪些地方。
另一个重灾区是密码重置链接。很多系统生成的token里直接编码了用户邮箱和旧密码哈希,比如base64(email:hash)。攻击者拿到链接后解码就能看到哈希,如果哈希是MD5且没加盐,彩虹表几秒就破。正确的做法是服务端存一个随机token,跟用户ID关联,设置短有效期,且token本身不包含任何用户信息。
我自己的检查清单里还有一条:看错误处理。当用户输入错误密码时,接口返回的报错信息会不会泄露“密码长度不对”还是“密码包含非法字符”?这些细节能让攻击者缩小暴力破解范围。更隐蔽的是时间侧信道——比较密码时用==而不是hmac.compare_digest,响应时间差几毫秒,足够推断出前缀匹配了多少位。
说回开头那个SaaS产品。我提交漏洞报告后,他们花了三天修复:日志脱敏、Kibana权限收紧、密码修改接口改成POST body。但真正让我不安的是,他们的CTO在邮件里写:“我们一直以为HTTPS就够了。”加密传输解决不了日志明文、内存转储、以及开发人员随手console.log的习惯。密码安全是条链子,最弱的那一环往往不是加密算法,而是某个人在凌晨三点调试时留下的那行日志。