必须在服务端用bleach.clean()白名单过滤HTML,前端净化仅为补充;禁用|safe直接输出,需显式配置tags、attributes及protocols;禁止strip_tags/正则等不可靠方案;属性值须二次转义;富文本白名单须按业务最小权限控制。

直接渲染用户提交的HTML内容等于把<script>标签当午餐盒打开——浏览器会照单全收。必须在服务端做白名单过滤,前端净化只是锦上添花,不能替代后端校验。</script>
用 bleach.clean() 限制 Django 中的 HTML 标签
别依赖 |safe 过滤器,它不检查内容是否真安全。Django 模板里用 |safe 前,必须先在视图或模型层用 bleach.clean() 处理原始字符串。
-
bleach.clean()默认只保留极简标签(p、br、strong等),不带属性;若需a标签,得显式传tags=['a', 'p']和attributes={'a': ['href']} - 对
href属性必须加协议白名单:用protocols=['http', 'https', '/'],否则javascript:alert(1)会逃逸 - 别设
strip=True后又手动拼接 HTML——它会删掉所有非法标签但保留其文本内容,若原始输入是<script>X</script>,结果变成X,看似安全,实则可能破坏语义或被用于混淆
为什么不用 strip_tags() 或正则匹配
PHP 的 strip_tags() 和任何正则方案都不可信。它们不解析 DOM,只做字符串替换,完全无法识别 <p onclick="fetch('/api/leak')"> 这类带危险属性的合法标签。
-
strip_tags($input, '<p><strong>')</strong></p><div class="aritcle_card flexRow"> <div class="artcardd flexRow"> <a class="aritcle_card_img" href="/xiazai/skill8272" title="anything-to-html"><img src="https://img.php.cn/upload/skill/000/000/081/179195588450431.jpg" alt="anything-to-html" ></a> <div class="aritcle_card_info flexColumn"> <a href="/xiazai/skill8272" title="anything-to-html">anything-to-html</a> <p>将调研报告、方案对比、数据分析、需求梳理、会议纪要、设计说明、代码评审、学习笔记等任务产物输出为可在浏览器直接打开的单文件 HTML,替代 Markdown 作为最终交付物。Markdown 在信息密度、视觉清晰度和可分享性上已跟不上 AI 产物的丰富度,HTML 能同时承载排版、表格、SVG 等。</p> </div> <a href="/xiazai/skill8272" title="anything-to-html" class="aritcle_card_btn flexRow flexcenter"><b></b><span>下载</span> </a> </div> </div>允许<p></p>,但不会删掉里面的onclick、onmouseover等事件处理器 - 正则无法处理嵌套、注释、CDATA、自闭合标签异常(如
<img src=x onerror=alert(1)>) -
FILTER_SANITIZE_STRING已在 PHP 8.1+ 中废弃,继续用等于主动关闭安全防护
输出到 HTML 属性时必须用上下文敏感转义
哪怕内容已通过 bleach.clean() 过滤,一旦插入到 value=""、title="" 或 data-xxx="" 这类属性中,仍需二次转义——因为双引号本身不是 HTML 标签,但会提前截断属性值。
立即学习“前端免费学习笔记(深入)”;
- 在 PHP 中,一律用
htmlspecialchars($value, ENT_QUOTES, 'UTF-8'),ENT_QUOTES确保单双引号都被编码 - 在 Python 模板(Jinja2)中,用
{{ value | e }}而非{{ value | safe }},除非你 100% 确认该变量已是 bleach 净化后的结果且只用于 HTML body 文本上下文 - 切勿把净化后的 HTML 直接塞进
onclick="..."或href="javascript:..."——这类上下文需要 JavaScript 字符串转义或 URL 编码,不是 HTML 实体编码能覆盖的
最常被忽略的一点:富文本内容的「允许标签」清单必须和业务强绑定。比如评论区不需要 iframe 或 table,就绝不在白名单里加;加了就得配套校验 src、width、height 等属性合法性。没有通用的安全配置,只有具体场景下的最小权限控制。


















