安全指南
加了登录就以为安全了吗 — 认证与授权的区别
认证(你是谁)与授权(你可以做什么)是两回事。只加了登录、却没有按所有者(user_id)收窄数据,就会出现「登录 = 看到全部数据」的事故。本文讲解成因与防御。
「我加了登录功能,所以现在已经受保护了」——这是一个危险的想当然。实际上,登录(认证)和权限(授权)是两回事,一旦把这里弄混,就会发生只要登录,所有人都能看到包括他人在内的全部数据的事故。这里不写攻击步骤,只讲成因与防御。
认证与授权是两回事
它们很容易被混淆,但角色完全不同。
只有认证(常见的误解)
- 觉得「能登录 = 安全」
- 把登录后的数据读取当成对所有人都一样
- 结果:只要登录,所有人都能到达全部数据
认证 + 授权(本应如此)
- 用认证确认「你是谁」
- 用授权限定到「这个人可以触碰的数据」
- 数据读取始终按本人所有的范围收窄
认证是「入口处的身份核验」,授权是「控制可以进入哪些房间」。登录功能只保证前者。「谁可以触碰哪些数据」需要你另外编写。
「看到全部数据」在真实应用中是怎么发生的
在实战中,同一个漏洞在多个彼此独立、各自自建的系统里反复出现。无论是个人用的任务/笔记管理应用,还是小规模的商品・订单管理系统(带有外部 API 集成),成因都是同样两个缺陷的叠加。
① 注册是开放的(而且有两条入口)
认证脚手架(例如 Laravel 的 Breeze/Jetstream 之类的东西)往往默认就把注册页面设为公开。而当注册路由在 UI 库一侧和认证包一侧重复存在时,堵住一处后 /register 依然留着。知道 URL 的陌生人就能注册。
② 有认证但没有授权(缺少所有者收窄)
数据表没有所有者列(user_id),或者列表・读取・更新・删除的查询没有收窄到当前用户。于是「登录 = 可访问全部数据」。如果应用在注册后立即自动登录,陌生人一注册就会落到他人的数据列表上。
结果:认证 ≠ 授权 的事故
入口(登录)是有的,但房间的钥匙(授权)没有。因为陌生人表现得像「一个正常登录的用户」,几乎连未授权访问的痕迹都不会留下。
越是牵涉业务数据,爆炸半径就越大(客户资料、订单、集成用的 API 密钥都可能受波及)。而且用同样方式搭建的另一套系统,很可能也存在同样的漏洞。
如何防御
数据必须按所有者收窄
把每一次读写都限定为「当前登录用户自己的那些行」。通过 ORM 的全局 scope 或授权策略(policy/gate),对读取/更新/删除的全部操作强制加上所有者条件。如果表里没有所有者列,先从那里重新设计。
关闭不需要的注册——并怀疑入口重复
在个人/内部应用上停用注册。要通过 HTTP 实测 /register 之类确实返回 404,并检查 UI 库和认证包两侧的注册路由。
对管理面做纵深防御
对于内部/管理用途,不要只靠认证——叠加 IP 限制、Basic 认证等。就算一道墙被攻破,下一道也能拦住。
在事故*发生之前*就备好审计与访问日志
如果没有「谁、在何时、从哪个 IP、做了什么」,事后就无法还原「什么被看到了」。至少要保留登录成败的审计日志,并保留/轮转 Web 访问日志。
检测新注册与异常
这次的核心教训是「很长时间都没被察觉」。加入能尽早察觉的机制——新用户注册的通知、定期的用户/数据盘点。没有检测,漏洞就会一直静静地开着。
本站的观点:库只保证到『你是谁』为止
认证库替你操心的只到「你是谁」为止,再往后就没有了。「这个人可以做什么」——除非作为设计者的你亲手写出来,否则它并不存在。而且——在一套系统中发现的结构性缺陷,很可能也存在于用同样方式搭建的其他系统里。不要修好一件就收手,要横向排查所有同类结构的搭建。当你无法判断某样东西是否被读取过时,正确的姿态是把它当作已被读取,并对暴露的秘密进行失效/轮换。
接下来阅读
- 术语:IDOR(访问控制缺陷)是什么(典型:指定他人的 ID 就能到达他人的数据)
- 落地:按框架讲安全(在每个框架里把授权写在哪里)
- 相关:别把秘密文件放进公开目录(检查 token/credentials/.env 的暴露点)
- 入门:从零开始保护秘密 / 基础:最低限度的安全检查清单
出处
- OWASP Top 10 (2021) A01: Broken Access Control: owasp.org
- OWASP Authorization Cheat Sheet: cheatsheetseries.owasp.org
FAQ
Q认证(Authentication)与授权(Authorization)有什么区别?
认证是确认『你是谁』(登录)。授权是决定『这个人可以做什么』(权限)。打个比方,认证是在大楼入口做身份核验,授权则是控制『这个人可以进入哪些房间』。只加了登录功能,只实现了认证,而授权(谁可以触碰哪些数据)需要你另外编写。把两者混为一谈,就会落到『只要登录,就能看到全部数据』的地步。
Q明明有登录功能,为什么所有数据都能被看到?
因为取数据的查询没有收窄到『只取当前登录本人自己的那些行』。只要在列表、读取、更新、删除中的任何一处漏掉了所有者(user_id)条件,一旦登录通过,包括他人在内的全部行都会被返回。这是一个与是否存在认证无关的设计漏洞,正是 OWASP 排名第一的 Broken Access Control(访问控制缺陷)的典型。
Q如果是个人工具或内部应用,注册一直开着也没关系吧?
很危险。许多认证脚手架默认就把注册(如 /register)设为公开。只要知道 URL,陌生人就能注册;一旦没有授权,就会直接到达里面的数据。而且注册入口有时是重复的(UI 库和认证包各有一套),只堵住一处并不能关闭它。请立即停用不需要的注册,并在管理面叠加 IP 限制等纵深防御。