私有文章的评论被未授权读到,权限该在列表层拦还是详情层拦
私有文章的评论被未授权读到,多半是因为权限只在详情层判了,列表层忘了判。WordPress 7.1.3 的官方安全修复里就有这一条:未授权披露私有与未发布文章的评论(Unauthenticated disclosure of comments on private & unpublished posts)。它的教训是——列表层和详情层都得拦,而且附属资源最容易漏。
为什么附属资源最容易漏
一篇设为私有的文章,主对象本身受权限保护,直接访问会被挡住。但评论、附件、留言这些「挂在主对象下面的子集合」,往往有自己独立的查询入口。开发者在给主对象加权限时,很容易默认子集会跟着一起被保护,可事实是主对象的可见性不会自动向下传递。
于是攻击路径变得简单:不碰文章,直接请求它的评论列表。如果评论接口只按「文章 ID」过滤,而没把「当前用户是否有权看这篇文章」一起带进条件,私有文章的评论就被匿名读走了。附属资源是这套模型里最常被忽略的一层。
只在详情层判权限会留什么口子
只在详情层判权限,等于假设所有访问都会先落到详情页。但接口世界里,列表、搜索、批量、嵌入式端点都是绕过详情的入口。只要有一个入口没判,主对象的隐私就从那条缝漏出去。
| 拦截位置 | 单独使用的后果 |
|---|---|
| 只在详情层判 | 列表、搜索等入口可被直接调用,绕过详情拿到子集数据 |
| 只在列表层判 | 详情页若另走一套查询,仍可能返回不该给的内容 |
| 两层都判且默认拒绝 | 无论从哪个入口进来,都要重新确认权限 |
稳的做法是把默认行为设成拒绝:没有明确授权就返回空,而不是默认放行再靠个别页面补救。越权类问题靠默认拒绝,比靠逐个接口补白名单要稳得多。
列表层过滤要带哪些条件
列表接口最关键的,是把主对象的状态和当前用户的权限一起加进查询条件,而不是只按父 ID 取子集。具体要带三类条件:这条附属资源所属主对象的可见状态、当前调用者对该主对象的访问权、以及资源自身的发布状态。三者同时成立才返回。
换句话说,评论列表不能只问「这篇文章下有哪些评论」,还要问「这个用户有没有资格看这篇文章,如果有,才继续列」。把权限判断写进查询过滤,而不是取回全部再在展示层删减,才能防止数据在传输或缓存环节就被看到。
站内权限分组与文档状态怎么配合
AnQiCMS 用两条正交的线来界定可见范围:一条是用户管理与 VIP 分组,为不同用户组设置不同的访问权限,决定接口返回时这个调用者能看哪些内容;另一条是文档状态——正式文档、草稿、待发布、回收站四种。草稿可以带 preview 标记供预览,但它并不等于对所有人公开;删除正式文档是移入回收站而非物理删除,可见范围同样要跟着状态走。
两条线要配合:接口在返回评论、附件这类子集时,先看文档状态是否为对外可见的正式态,再看调用者的用户组是否有权访问,缺一个条件就不返回。身份这一层则由内置的 JWT 认证把守,先确认调用方是谁,再谈他能不能读。这样主对象和附属资源走的是同一套判断,不会出现「文章挡住了、评论漏了」的错位。
常见问题
问:文章已经是私有了,评论为什么会单独泄露? 答:评论有独立查询入口,主对象可见性不自动传给子集,列表接口要重新判权限。
问:只在详情页加权限判断够不够? 答:不够。列表、搜索、批量入口都能绕过详情,应两层都判并默认拒绝。
问:草稿预览链接要注意什么? 答:预览范围要单独界定,别让它变成对所有人可读,用户组权限仍要生效。
问:越权读取怎么收敛最稳? 答:把状态与所属对象权限一起写进查询过滤,采用默认拒绝而非逐个白名单。