Blade的{{ }}仅防HTML文本内容区XSS,通过htmlspecialchars转义<、>、"、&四个字符;不防属性值、JS、CSS、URL等上下文XSS,无法校验协议、闭合或结构,富文本必须用HTMLPurifier预净化。

Blade 的 {{ }} 默认转义只防 HTML 内容区 XSS,不覆盖属性、JS、CSS、URL 等上下文——直接依赖它,等于在多数富文本或动态输出场景里留了后门。
Blade {{ }} 到底能防什么、不能防什么
它本质是调用 htmlspecialchars($value, ENT_QUOTES, 'UTF-8'),只处理这 4 个字符:、<code>>、"、&。所以:
- 用户输入
<script>alert(1)</script>→ 渲染为纯文本,安全 - 但输入
<img src=x onerror=alert(1)>→ 转义后仍是完整标签结构,onerror属性在浏览器解析时仍可能触发 - 若输出到
href="{{ $url }}"或style="color: {{ $color }}",{{ }}不校验协议、不转义 CSS 关键字、不处理引号闭合,照样可逃逸
HTML 属性值里输出变量必须单独编码
比如 <input value="{{ $val }}">,$val 是 " onclick="alert(1),{{ }} 只转义引号里的 ,但不会补全属性闭合或对 <code>onclick 做剥离——浏览器一解析就执行。
- 正确做法:用
htmlspecialchars($val, ENT_QUOTES | ENT_HTML5, 'UTF-8')显式编码,且确保属性值被双/单引号包裹 - 更稳妥:改用 Laravel 的
e()辅助函数,它和{{ }}底层一致,但可主动控制时机(如在 accessor 中提前处理) - 绝对别写
<input value="{!! $val !!}">—— 这等于把解析权完全交给浏览器
JS 上下文输出必须用 json_encode(),不是 htmlspecialchars()
常见错误写法:<script>var name = "{{ $name }}";</script>。一旦 $name 含换行、双引号或 字符串,JS 语法直接崩,还可能注入执行逻辑。
- 正确方式:
<script>var name = @json($name);</script>(Laravel 内置,自动加引号+转义) - 或手动:
var name = <?php echo json_encode($name, JSON_UNESCAPED_UNICODE | JSON_HEX_TAG); ?>; - 注意:
json_encode()输出的是 JS 字面量,不是 HTML 字符串;混用htmlspecialchars()会导致双重编码(如&变成&)
富文本输出必须走 HTMLPurifier,不能靠 {!! !!} + strip_tags()
后台编辑器、评论区支持格式内容,意味着用户可提交带属性的标签。此时:
-
strip_tags()只删标签,不碰onerror、javascript:协议、data:URI 等危险属性,无效 -
{!! !!}直接输出未净化 HTML,等同于开放 XSS 入口 - 必须用
Mews\Purifier::clean($html, 'default'),并在配置中显式限制:'HTML.Allowed' => 'p,b,i,u,a[href|title],img[src|alt]'、'URI.AllowedSchemes' => ['http', 'https']、'Attr.EnableID' => false
最易被忽略的一点:净化必须在入库前或首次渲染前做,而不是每次请求都调用——否则性能拖垮,且无法保证一致性。


















