Revel模板防XSS需分上下文编码:esc仅适用于HTML文本,不适用于href、onclick等属性或JS上下文;raw函数高危,仅限后端已净化的富文本;URL和JS上下文须单独编码,动态属性名须白名单校验。

Revel 模板默认不转义任何输出,{{.UserName}} 会原样插入 HTML —— 如果值含 <script></script>,它就会执行。防注入不是靠“加个函数就完事”,而是分上下文选对编码方式、避开 raw 陷阱、拒绝拼接逻辑。
esc 函数只适用于普通 HTML 文本内容
esc 是 Revel 内置的 HTML 实体转义函数,等效于 html.EscapeString(),仅处理 、<code>>、&、"、' 这五个字符。
- 适合场景:
<p>{{esc .Bio}}</p>→ 用户输入<div>Hello</div>渲染为纯文本 - 不适用场景:放在
href、onclick、src等属性或 JS 上下文中,esc不会编码javascript:或javascript - 常见错误:写
<a href="%7B%7Besc%20.Url%7D%7D"></a>,若.Url是javascript:alert(1),esc不动它,XSS 立刻触发
raw 函数等于主动放弃防护
raw 完全绕过转义,传入什么就输出什么。它不是“高级用法”,而是高危操作开关。
- 唯一合理使用条件:后端已用
DOMPurify或类似库清洗过富文本,并确认无<script></script>、onerror、href="javascript:"等危险模式 - 绝对禁止:把用户原始输入(如评论、昵称、搜索关键词)直接塞进
{{raw .Input}} - 隐蔽坑点:模板里混用
{{raw .SafeHTML}}和{{esc .UnsafeText}},一旦.SafeHTML被污染,整个页面失守
属性值、URL、JS 上下文必须单独编码
HTML 属性和脚本上下文的编码规则和普通文本完全不同,不能指望一个 esc 全包圆。
- 双引号属性(如
title="{{esc .Title}}"):安全,esc会把"变成",防止属性提前闭合 - 单引号属性(如
data-id='{{esc .ID}}'):不推荐,esc不处理'的十六进制编码(如'),攻击者可绕过 - URL 属性(
href、src):必须在控制器中用url.QueryEscape()或net/url.URL.EscapedPath()编码,再传入模板;不能只靠esc - 内联 JS(如
onclick="doSomething('{{.Value}}')"):禁止!应改用 data 属性 + 外部 JS 绑定,或用 JSON 编码后js.Marshal输出
动态字段名/属性名必须白名单校验
Revel 模板不支持变量插槽(比如 {{.Fields[.UserChoice]}}),但开发者常在控制器里拼接字段名或 class 名,这一步极易引入反射型 XSS。
- 错误做法:
tmplVars["class"] = "user-" + userInput→ 模板里写<div class="{{.class}}"> <li>正确做法:用 map 白名单校验,<code>validClasses := map[string]bool{"user-admin": true, "user-guest": true},查不到就 fallback 到默认值 - 更稳妥:所有动态 class、id、data-* 属性,都走预定义常量或枚举,避免运行时拼接字符串
最易被忽略的是 URL 和 JS 上下文 —— 它们根本不在 esc 覆盖范围内,而开发者又习惯性复用同一变量。别让一个 href="{{.Url}}" 毁掉整页防护。


















