首页/新闻资讯/正文详情

Twig raw 过滤器:标记输出为“安全值“以绕过自动转义

发布时间:2026/9/25 3:33:22 来源:云帆数科 栏目:资讯中心
Twig raw 过滤器:标记输出为“安全值“以绕过自动转义
后端【免费下载链接】TwigTwig, the flexible, fast, and secure template language for PHP项目地址https://gitcode.com/gh_mirrors/tw/Twig点击查看免费下载raw是 Twig 中用于标记变量为安全值的过滤器在启用了自动转义auto-escaping的环境中被raw作为最后一个过滤器处理的变量将按原样输出、不再被转义。本文基于 raw 过滤器官方文档 展开并结合当前仓库源码解释它零运行时成本的实现原理、raw必须位于过滤器链末端的原因以及它与Markup类、escape过滤器等安全机制的协同关系帮助你在模板中安全地输出已转义内容如json_encode的结果而不产生二次转义。一、raw 过滤器的基本用法原始文档doc/filters/raw.rst给出的核心语义只有一句话raw过滤器将值标记为 safe安全。具体含义是在启用自动转义的环境中当raw是该变量上应用的最后一个过滤器时该变量不会被转义{% autoescape %} {{ var|raw }} {# var wont be escaped #} {% endautoescape %}autoescape标签本身的作用域与策略html、js、false等详见 autoescape 标签文档。文档中强调了一个容易忽略的细节ifrawis the last filter applied to it当raw是最后应用的过滤器时。这不是文档措辞的随意性而是由安全值safe analysis的编译期推导规则决定的这一点会在第三节结合源码解释。一个实用的推论在未启用自动转义的环境中{{ var|raw }}与{{ var }}的输出完全相同——因为raw本质上是一个编译期的身份变换它不会改变值本身只是向编译器声明这个值已经安全。二、源码实现raw 是一个零成本的编译期标记2.1 过滤器注册没有可调用对象raw过滤器并非由 CoreExtension 提供而是由负责转义体系的 EscaperExtension 注册new TwigFilter(escape, [EscaperRuntime::class, escape], [is_safe_callback [self::class, escapeFilterIsSafe]]), new TwigFilter(e, [EscaperRuntime::class, escape], [is_safe_callback [self::class, escapeFilterIsSafe]]), new TwigFilter(raw, null, [is_safe [all], node_class RawFilter::class]),这一行注册代码包含三个关键信息callable为nullraw没有任何运行时实现它不需要对值做转换is_safe [all]静态声明该过滤器对所有转义策略html、js、css、url等都安全这正是EscaperNodeVisitor判断该表达式是否需要再包一层escape的依据node_class RawFilter::class解析raw时生成的不是通用的FilterExpression节点而是专门的 RawFilter 节点。2.2 RawFiltercompile 时只做透传RawFilter 继承自FilterExpression其compile()方法的全部逻辑只有一行public function compile(Compiler $compiler): void { $compiler-subcompile($this-getNode(node)); }即编译{{ var|raw }}时只会把内层表达式var编译进输出不会生成任何调用raw函数的 PHP 代码。换言之raw过滤器在运行时的开销为零它的全部效果都发生在模板编译阶段——由节点访问器NodeVisitor读取is_safe [all]这个元数据来决定是否跳过转义包裹。另外FilterExpression::compile() 中保留了一段针对raw的兼容分支并自 Twig 3.11 起发出弃用提示Creating the raw filter via FilterExpression is deprecated; use RawFilter instead.。这说明当前版本中节点访问器必须为raw构造RawFilter实例而不能走通用过滤器节点路径。三、为什么 raw 必须是最后一个过滤器安全值的推导规则3.1 转义决策发生在编译期是否对某个PrintNode即{{ ... }}输出语句包上escape过滤器由 EscaperNodeVisitor 在遍历 AST 时决定进入AutoEscapeNode时把当前转义策略压入statusStackenterNode离开时弹出没有autoescape标签时则回落到环境的默认策略getDefaultStrategy可通过FileExtensionEscapingStrategy等机制按文件扩展名设置见 src/FileExtensionEscapingStrategy.php处理PrintNode时调用escapeExpression()核心判断是isSafeFor($type, $expression, $env)leaveNode、isSafeForreturn \in_array($type, $safe, true) || \in_array(all, $safe, true);只要表达式的安全集合包含当前策略或all就跳过escape包裹。而表达式的安全集合由 SafeAnalysisNodeVisitor 计算。3.2 过滤器链的安全集合是从外到内传播的SafeAnalysisNodeVisitor 对 FilterExpression 的处理 揭示了raw位置敏感性的根源$safe $filter-getSafe($node-getNode(arguments)); // ... if (!$safe) { $safe $this-intersectSafe($this-getSafe($node-getNode(node)), $filter-getPreservesSafety()); } $this-setSafe($node, $safe);若过滤器自身声明了安全策略如raw声明了[all]整个过滤器表达式就直接安全若外层过滤器未声明安全如upper、e则安全集合等于内层表达式的安全集合与外层过滤器保持安全的能力preservesSafety的交集——通常交集为空。由此可以精确理解文档中raw 是最后一个过滤器才有效的含义{{ safeHtml|raw }} {# raw 在最外层 → 安全集合为 [all] → 不转义 #} {{ safeHtml|raw|upper }} {# 外层 upper 未声明安全交集为空 → 仍会被转义 #} {{ var|e|raw }} {# raw 在最外层 → 不再转义e 的转义仍保留在值里 #}这也是为什么raw常被写在过滤器链的末尾——它是最终裁决而不是中间步骤。3.3 与 escape 过滤器的协同escape别名e过滤器注册时使用了is_safe_callback见 EscaperExtension运行时实现位于 EscaperRuntime。autoescape 文档 还提到两条相关规则与raw的用法直接相关不会二次转义Twig 足够智能不会用escape过滤器对已按相同策略转义过的值再次转义静态表达式不转义{% set hello strongHello/strong %}{{ hello }}会原样输出strongHello/strong因为编译器能识别字面量是安全的。四、与 Markup 类、安全类注册表的对比除模板内用|raw标记外Twig 还支持在PHP 侧声明一个值是安全的典型代表是 Markup 类class Markup implements \Countable, \JsonSerializable, \Stringable { private $content; private ?string $charset; public function __construct($content, $charset) { ... } }其类注释src/Markup.php明确说明Markup实例及既有子类被视为已被判定可安全输出的内容在 Twig 沙箱中对其方法调用与属性访问会绕过SecurityPolicy的允许清单。此外EscaperRuntime 维护了一份安全类注册表safeClasses/safeLookup通过addSafeClass()注册从源码结构看运行时转义会借助该注册表识别Markup这类天生安全的值并跳过转义。可以这样对比两种声明安全的途径途径位置适用场景{{ value\|raw }}模板中值在模板上下文里已确认安全如缓存的 HTML 片段、json_encode输出Markup实例 /addSafeClass()PHP 代码中在业务层就确定某类对象的内容已转义统一注册后模板侧无需再写raw注意raw只影响转义决策它本身不改变值的类型而Markup还会附带沙箱信任等运行时语义。两者配合时Markup值再经过|raw输出仍是安全的但实践中二选一即可。五、实战示例来自集成测试的真实用例当前仓库的集成测试用少量用例覆盖了raw的典型使用姿势基础用例tests/Fixtures/filters/raw.test{{ br|raw }}数据为[br br]期望输出br——变量排除了自动转义。JSON 场景tests/Fixtures/filters/json_encode.test{{ foo|json_encode|raw }}。json_encode生成的 JSON 串本身不是 HTML 安全内容若在autoescape块内直接输出会被再次转义如引号变成quot;因此需要|raw收尾。URL 场景tests/Fixtures/filters/urlencode.test{{ {...}|url_encode|raw }}同理防止等字符被 HTML 策略二次转义。与apply标签组合tests/Fixtures/tags/apply/json_encode.test{% apply json_encode|raw %}test{% endapply %}说明raw位于apply过滤器链的末端与文档最后一个过滤器的规则一致。这些用例由 tests/IntegrationTest.php 统一驱动执行可直接作为验证raw行为的参照。六、安全注意事项raw是一份责任声明不是转义豁免开关。它告诉编译器这个值已经安全但编译器无法替你验证值的真实性——对未净化的用户输入使用|raw会直接引入 XSS 风险。默认应让自动转义生效仅在内容确实已经转义如经过escape处理并存储、json_encode序列化、白名单模板生成的 HTML 片段时才使用raw。注意策略匹配。raw的安全声明是[all]即声明对所有策略html、js、css、url都安全。若一段内容只做过 HTML 转义却在{% autoescape js %}块中用|raw输出它实际上并未按js策略转义——此时输出是否安全取决于内容本身。优先选择 PHP 侧注册安全类如果某类对象的内容在业务层就保证已转义用 Markup 或EscaperRuntime::addSafeClass()统一声明可以显著减少模板中散落各处的|raw让安全边界集中在代码侧管理。小结raw过滤器的完整工作链路是EscaperExtension 以is_safe [all]注册且callable为null→ 解析器生成 RawFilter 节点编译时仅透传子表达式零运行时开销→ SafeAnalysisNodeVisitor 将最外层过滤器安全集合为[all]记入 AST → EscaperNodeVisitor 据此跳过escape包裹。理解这条链路后你就能准确把握为什么raw要放在过滤器链末尾并把它与Markup、escape、autoescape策略正确组合在防转义过度与防 XSS之间取得平衡。赞分享后端【免费下载链接】TwigTwig, the flexible, fast, and secure template language for PHP项目地址https://gitcode.com/gh_mirrors/tw/Twig点击查看免费下载相关推荐Twig nl2br 过滤器详解HTML 换行转换与自动转义的前置转义机制Twig nl2br 过滤器详解HTML 换行转换与自动转义的前置转义机制 本文以 Twig 官方文档中的 nl2br 过滤器为切入点完整讲解其在模板中的用后端终极LFI过滤器绕过指南2024年最新Payload与实战技巧终极LFI过滤器绕过指南2024年最新Payload与实战技巧 Local File Inclusion本地文件包含LFI漏洞是Web应用安全中最常见且网络安全应用安全渗透测试告别手动复制粘贴3步高效获取国家中小学智慧教育平台电子课本告别手动复制粘贴3步高效获取国家中小学智慧教育平台电子课本 你是否曾经为了获取电子课本而反复复制粘贴网址是否因为下载速度慢、文件命名混乱而烦恼tchMat网页爬虫教育上一篇戴森球计划8000蓝图库从零开始打造高效星际工厂的终极指南下一篇conda init 命令详解Shell 初始化机制、参数全解与源码级实现分析创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

相关推荐

Spring Boot + Vue酒店预订系统毕业设计:从选型到答辩全流程实战
Spring Boot + Vue酒店预订系统毕业设计:从选型到答辩全流程实战

每年毕业设计选题季,酒店预订系统都是计算机方向的高频题。我见过太多人拿到这类题目,第一反应是“这不就是个CRUD吗?有什么好写的”,结果真动起手来,卡在前后端联调、表结构设计、并发冲突这些地方,一耗就… · 2026/9/25 3:33:22

gsd-core Changeset-fragment 工作流:从 CHANGELOG 冲突治理到自动化发布编排
gsd-core Changeset-fragment 工作流:从 CHANGELOG 冲突治理到自动化发布编排

【免费下载链接】gsd-core Git. Ship. Done - Core 项目地址: https://gitcode.com/gh_mirrors/ge/gsd-core 点击查看 免费下载 本指南围绕 gsd-core 仓库引入的 Changeset-fragment 工作流展开,讲解如何让每个 PR 通过一个独立变更片段(fra… · 2026/9/25 3:33:22

Jlink烧录仿真全攻略:从驱动安装到故障排查
Jlink烧录仿真全攻略:从驱动安装到故障排查

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/25 3:33:22

外墙墙体渗水维修师傅 好工匠防水 高空作业 外墙裂缝修补专用材料
外墙墙体渗水维修师傅 好工匠防水 高空作业 外墙裂缝修补专用材料

随着国内建筑使用年限逐步增加,以及北方特殊气候对建筑外墙的持续侵蚀,外墙防水维修市场的需求正在持续增长。京津冀区域受北方冬季冻融循环、春季持续返潮、沿海区域盐蚀、雨季强降水的多重影响,外墙渗水问题成为民居、商用建筑、工业厂房都… · 2026/9/25 8:52:11

ESP32-S3桌面AI机器人实战:全双工语音与视觉多模态交互全解析
ESP32-S3桌面AI机器人实战:全双工语音与视觉多模态交互全解析

EchoEar喵伴这个项目,实际做下来我最大的感受是:它表面上看是个桌面小玩具,本质上却是一道特别扎手的嵌入式工程题。要在ESP32-S3这颗MCU上同时搞定全双工语音交互、摄像头视觉采集、云端大模型对话,还要保证用户能随时打断机器人… · 2026/9/25 8:51:53

GD32高级定时器互补PWM输出与死区控制实战
GD32高级定时器互补PWM输出与死区控制实战

写GD32的高级定时器,绕不开三相电机控制、全桥逆变、UPS这类场景。做这类项目的人,百分之九十九都躲不过一个需求:要输出两路相位相反、中间还夹着一小段“空白”的PWM,而且这段空白还得精确可控。这段空白就是死区,控… · 2026/9/25 8:51:53

树莓派5 GPIO 5V引脚供电实操:方案选型、压力测试与避坑指南
树莓派5 GPIO 5V引脚供电实操:方案选型、压力测试与避坑指南

这段时间身边好几个玩树莓派5的朋友都跑来问我同一个问题:能不能直接通过GPIO的5V引脚给板子供电?有的想把树莓派5塞进无人机或者小车里,不想带着原装Type-C电源线;有的是想省一个插座,从稳压模块直接拉电;… · 2026/9/25 8:51:47

ng-zorro-antd Affix(固钉)组件完全指南:从 API 配置到源码级实现原理
ng-zorro-antd Affix(固钉)组件完全指南:从 API 配置到源码级实现原理

UI组件前端 【免费下载链接】ng-zorro-antd Angular UI Component Library based on Ant Design 项目地址: https://gitcode.com/gh_mirrors/ng/ng-zorro-antd 点击查看 免费下载 Affix(固钉)是 ng-zorro-antd 提供的页面固定组件&#xff0… · 2026/9/25 8:51:41

黏菌算法SMA优化SVM/SVR/LSSVM参数:回归预测调参实战
黏菌算法SMA优化SVM/SVR/LSSVM参数:回归预测调参实战

玩SVM的朋友都知道,模型性能的下限靠数据,上限靠调参。尤其做回归预测时,惩罚参数c和核函数参数这两个参数一旦选不好,特征工程做得再漂亮也是白搭。我这边用的方案是黏菌算法SMA去自动搜索SVM、SVR还有LSSVM的惩罚参数c和核函数参… · 2026/9/25 8:51:40

数值优化(Numerical Optimization)学习系列-03-共轭梯度方法(Conjugate Gradient)
数值优化(Numerical Optimization)学习系列-03-共轭梯度方法(Conjugate Gradient)

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/25 1:00:31

创维E900V22D刷机全攻略:S905L3SB芯片兼容性解析与救砖实战
创维E900V22D刷机全攻略:S905L3SB芯片兼容性解析与救砖实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/25 1:00:31

MQTT协议原理与Broker服务器搭建实战:从Mosquitto到EMQX
MQTT协议原理与Broker服务器搭建实战:从Mosquitto到EMQX

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/25 1:00:37

了解更多?预约专属演示

我们的顾问将为您一对一讲解产品与方案

企业微信二维码