|
去年秋天,接口漏洞一个做电商的接口漏洞朋友半夜给我打电话。他的接口漏洞语气像是刚被人从被窝里拽出来——“订单全没了,后台显示支付成功,接口漏洞但数据库里一条记录都没有。接口漏洞”我让他把最近的接口漏洞代码提交记录发过来,翻了不到十分钟,接口漏洞就看到了问题:支付回调接口里,接口漏洞验证签名的接口漏洞逻辑被一个刚入职的实习生“优化”掉了。他觉得那段代码“看起来冗余”,接口漏洞删了之后测试环境一切正常,接口漏洞上线三天后炸了。接口漏洞 接口漏洞这件事,接口漏洞最麻烦的接口漏洞地方在于它往往不来自什么高深的技术缺陷。恰恰相反,接口漏洞它常常藏在那些“看起来没问题”的地方。签名验证、参数校验、权限判断——这些代码写起来枯燥,读起来也枯燥,于是总有人想动它们。动完之后测试环境跑一遍,没报错,就觉得万事大吉。可测试环境里哪来的恶意请求?哪来的重放攻击?哪来的越权访问? 我自己写过一个用户信息查询接口,当时图省事,直接把用户ID作为参数传给后端,没有做任何归属校验。逻辑上想得很简单:前端已经登录了,只会传自己的ID。但接口这东西,从来不是只给前端用的。任何人抓个包,把ID改成别人的,就能拿到别人的手机号和收货地址。这个漏洞在我自己的项目里躺了两个月,直到一个做安全的朋友随手试了一下,截了个图发给我。那张图我现在还留着,算是个提醒。 更隐蔽的是参数污染。比如一个接口接受role=user这样的参数来决定权限,有些框架在解析重复参数时,取的是第一个值,有些取最后一个,有些拼成数组。攻击者传role=user&role=admin,后端如果处理不当,就可能把普通用户当成管理员。这不是什么理论上的可能性,Spring Boot早期版本、PHP的某些框架都出过类似的问题。写代码的人觉得参数是前端传的,前端觉得参数是用户填的,用户里总有人不怀好意。 还有一种情况,是接口返回了不该返回的东西。我见过一个登录接口,验证失败时返回“用户名不存在”,验证成功但密码错误时返回“密码错误”。这两个提示分开看都很合理,合在一起就成了用户名枚举漏洞。攻击者拿一个字典跑一遍,就能知道哪些用户名是真实存在的。正确的做法是统一返回“用户名或密码错误”,但很多开发者觉得这样用户体验不好。安全性和便利性之间的取舍,往往就在这种小地方被忽略。 接口漏洞的另一个特点是,它不像SQL注入或者XSS那样有成熟的扫描工具能帮你兜底。很多逻辑漏洞需要理解业务才能发现。一个转账接口,如果只校验了余额是否充足,没校验转账金额是否为负数,那攻击者转一个负数金额,就能从对方账户扣钱。这种漏洞,自动化工具扫不出来,因为从代码层面看,逻辑是“通”的。 我后来养成了一个习惯:每写一个对外接口,先问自己三个问题。这个接口如果被恶意调用一百次,会发生什么?参数如果被篡改,最坏的结果是什么?返回的数据里,有没有用户不该看到的东西?这三个问题答不上来,接口就不算写完。听起来有点偏执,但比起半夜被电话叫醒,我宁愿白天多花二十分钟。 接口是系统和外界之间的门。门没锁好,里面装修得再漂亮也没用。 |