待审评论里的脚本在后台页面被执行,前台为什么看不到
待审评论里的脚本在后台页面被执行,前台为什么看不到?原因并不神秘:前台模板只渲染已通过审核的内容,未过审的那部分不会出现在访客地址上,于是这条注入路径只剩下一个触发场景——管理员打开评论管理页的那一刻。一旦队列里的文本被当成「已经收进数据库的数据」,而不是「仍然可控的外部输入」,脚本就在后台这一层被拼进了页面。
为什么问题偏偏在后台出现
后台列表页的渲染逻辑和前台不一样。前台要过状态判断,未过审的内容根本进不了模板;后台要让人能审,就必须把原文显示出来,否则管理员看不到待处理内容的全貌。「显示原文」这一步如果不做转义,评论正文里携带的脚本就会被当作页面的一部分执行。
第二层差别是会话权限。前台访客读到的是公开页面,脚本即使执行,能触达的也只是匿名会话;后台页面带着管理员登录状态,同一段脚本可以借这个会话去调接口、改配置。所以「前台看不到」不是风险变小,而是风险换了位置——从公开面挪到了权限最高的那一面。
待审内容同样是输入:一条公告该怎么读
WordPress 在 7.1.3 这个维护版本里,七处安全修复中就有一条评论管理页上的存储型跨站脚本,报告方明确写了「可通过待审评论触发」。这句话的价值不在于漏洞本身,而在于它给出的定性:待审状态只是业务状态,不是安全状态。
读这类公告时可以把三个字段分开看。第一是触发位置,写在管理页还是前台页面,决定影响的是谁的会话;第二是所需权限,匿名可提交和需要登录才能提交,处置顺序完全不同;第三是是否回移到旧分支,回移说明老版本还会继续收到同一修复。把这三项对着自家站点核一遍,比只记一个编号有用得多。
前台不可见就等于安全吗
不。至少有三条容易被忽略的路径:
- 通知与提醒邮件会把评论正文片段带进邮件体,管理员在邮件客户端里读到的仍是原文;
- 导出与备份文件里保存的是入库原文,任何「打开看一眼」的动作都可能触发执行;
- 站内接口把待审内容返回给前端脚本时,如果调用方直接写入页面,注入点又回到浏览器。
换句话说,「前台不显示」只挡住了访客这一条路径。真正要防的是所有把未过滤文本当成标记来渲染的地方,而这些地方大部分集中在后台和辅助链路上。
后台侧的三层收口
第一层是入库前的过滤。站内把留言与评论先进待审状态,敏感词过滤与关键词替换在这一步生效,命中的内容按规则替换或拦下。这层的意义是把明显脏的数据挡在展示之前,但黑名单式的替换本身并不完全可靠,不能只靠它。
第二层是输出时的转义。把「谁写的内容」和「这段内容是什么角色」分开处理:正文一律按纯文本转义,需要富文本的位置再单独声明。同一份数据在前台走一套输出函数、在后台走另一套,是最容易出漏子的写法——两处都应当默认转义,而不是默认信任。
第三层是权限与入口收敛。审核页只做审核该做的事,删除、状态切换这类动作要带校验,不应允许用一次普通页面加载就完成。后台地址本身不公开不等于鉴权,登录态、操作确认与接口鉴权是三件不同的事。站内提供 JWT 认证与 XSS、SQL 注入相关的内置防护,但这些都是「程序侧默认开启的一道」,不能替代输出转义这一步。
审核与过滤分别在哪一步生效
容易混淆的两处:内容审核管的是「这段内容能不能对外展示」,敏感词过滤管的是「展示时哪些词要替换或拦下」。前者依赖状态流转,后者依赖入库或输出环节的字符串处理。如果只在审核环节把关,被批准后仍然会把原始脚本原样送进后台;如果只做替换,换个编码写法就绕开了。
站内的文档状态分草稿、待发布、正式文档与回收站,评论和留言的待审流转与之分开管理。做安全自查时建议按状态把内容分成两组:一组会对外渲染,一组只在后台或通知里出现,两组都要核同一条规则——是否默认转义。草稿链接带预览参数的场景尤其要看一眼,能被外部打开的状态,都该按对外内容处理。
常见问题
问:把所有评论都关掉是不是最省事? 答:能关当然最省事,但业务上往往需要留言。折中做法是先审后发,同时把审核页的显示内容按纯文本处理,这样即使队列里有脏数据也不会被执行。
问:只在前台做转义够不够? 答:不够。前台转义保护的是访客,后台展示与通知链路保护的是管理员会话,而后者权限更高。同一份数据要在每个渲染点分别确认。
问:升级同行程序的公告能直接套到自己站上吗? 答:不能。公告给出的是该程序自身的代码路径与受影响版本,能借用的只是「待审内容也要按外部输入处理」这条定性,自家站点的渲染点要逐个核。
问:多久跟一次这类维护版本合适? 答:按公告里的严重度和触发位置排序。落在管理页、且匿名可提交的那一类优先跟进,其余合并到常规维护窗口。