评论框提交 <img src=x onerror=fetch('/api/me')> 保存成功,前台打开页面时控制台多出一条 /api/me 请求,响应里带着当前用户的手机号。第一次遇到会往复杂处想,其实漏掉的多是个小前提。
from bleach.sanitizer import Cleaner
cleaner = Cleaner(tags=['p','img','a'], attributes={'img':['src'],'a':['href']})
print(cleaner.clean('<img src=x onerror=alert(1)>'))
过滤用的黑名单只拦了 script 标签和 onclick,onerror 这类事件属性根本不在名单里。内容存库时是原样,渲染时又用了 innerHTML,浏览器解析到坏图就执行属性里的脚本。这一点先确认清楚,后面就不用反复试。
「富文本 onerror 过滤」这类改动最怕改一半,回滚脚本先准备好,再动生产。把这次用到的命令存进笔记,比记住结论有用,换个环境还能照着走。
改完隔一天再看一次,当场正常不代表能稳住。测试环境和线上的差别,往往就在版本号和那几行被忽略的配置上。
具体做法是,渲染改用 textContent 或框架自带的转义插值 别碰 innerHTML;服务端用成熟库按白名单过滤标签和属性 只留 href 和 src;把 on 开头的事件属性和 javascript 协议统一清掉;拿常见 payload 列表跑一遍 确认存进去的都被改写。
改完照着核一遍,同样 payload 再提交一次 库里存的是 <img src="x"> 整段 onerror 消失。
