Angular表达式注入指的是攻击者把恶意的Angular模板语法注入到页面中,被Angular框架当作合法表达式编译执行,从而实现读取敏感数据、发起XSS攻击等目的的安全漏洞。它本质上属于客户端模板注入(Client-Side Template Injection,简称CSTI)的一种。由于Angular应用大量依赖模板渲染,一旦用户可控的内容被框架重新编译,风险就会随之而来。

Angular表达式注入的基本原理
要理解这类漏洞,先要明白Angular的渲染机制。Angular的模板由HTML和绑定语法组成,框架在编译时会解析{{ }}插值表达式以及[property]、(event)等绑定语法,把表达式放到组件上下文中求值。正常情况下,模板在构建阶段或JIT编译阶段就已经确定,用户输入只会作为数据被渲染,不会被当作代码执行。
问题出在动态编译场景。如果开发者使用了JIT编译器,并通过某种方式把用户可控的字符串传入模板并触发编译,攻击者就可以构造类似{{constructor.constructor('alert(1)')()}}的表达式。这段语法会沿着原型链找到Function构造器,等价于动态执行了一段JavaScript代码。Angular早期版本内置了表达式沙箱,试图阻止这类访问,但从2.x某个版本起官方直接移除了沙箱,原因是沙箱不断被绕过且维护成本高,官方转而要求开发者保证模板内容永远来自可信来源。
沙箱移除意味着:只要恶意表达式能进入编译流程,就没有任何运行时防线了。因此防线必须前移到数据流层面,这也是后文防范措施的核心思路。
常见的注入场景与利用方式
第一种典型场景是服务端渲染时拼接模板。一些老旧的AngularJS(1.x)应用会把用户输入直接嵌入到包含插值表达式的HTML片段中再返回,比如搜索结果的回显位置出现了{{7*7}},如果页面渲染后显示49,就说明存在注入点,攻击者可以进一步升级payload获取cookie或调用接口。
第二种场景是客户端使用ng-bind-html或Angular中的bypassSecurityTrustHtml信任了不可信HTML。如果被信任的内容中包含模板语法,且恰好触发了重新编译,注入就会生效。此外还有通过URL参数、localStorage、postMessage等渠道把字符串写入DOM后又被框架处理的情况。
在AngularJS 1.x中,即便没有沙箱绕过,攻击者也常利用$eval、scope上的属性遍历等方式访问敏感对象。新版Angular中利用门槛更高,但JIT模式下风险依然真实存在。可以用一个简单示例说明JIT动态编译的风险:
import { Component, Compiler, Injector } from '@angular/core';
@Component({
selector: 'app-unsafe',
template: '<div [innerHTML]="html"></div>'
})
export class UnsafeComponent {
// 假设 userInput 来自URL参数,完全不可信
html = this.sanitizer.bypassSecurityTrustHtml(this.userInput);
constructor(private sanitizer: DomSanitizer) {}
}
上面的代码把用户输入标记为可信HTML,一旦输入内容包含绑定语法且进入编译流程,恶意表达式就会被求值。正确做法是永远不要对用户输入调用bypassSecurityTrustHtml,或者只信任经过严格白名单过滤的内容。
有效的防范措施
第一道防线是生产环境关闭JIT编译,改用AOT构建。AOT模式下模板在构建时就编译完成,运行时不存在动态编译能力,绝大多数注入手法会直接失效。在angular.json中配置"aot": true,并确保生产构建走默认的AOT管线。
第二道防线是数据与模板严格分离。渲染用户内容时优先使用插值绑定{{ userInput }}而非innerHTML,Angular会自动对插值内容做HTML转义。确实需要渲染富文本时,使用DomSanitizer.sanitize配合白名单,或者引入DOMPurify这类成熟的净化库先清洗再绑定:
import DOMPurify from 'dompurify';
// 先净化用户提交的富文本,再交给Angular渲染
renderUserContent(raw: string): SafeHtml {
const clean = DOMPurify.sanitize(raw, {
ALLOWED_TAGS: ['b', 'i', 'em', 'strong', 'a', 'p'],
ALLOWED_ATTR: ['href']
});
return this.sanitizer.bypassSecurityTrustHtml(clean);
}
第三道防线是针对AngularJS 1.x遗留系统的加固:升级到最新补丁版本,禁止对用户输入所在区域使用ng-include、动态模板指令,避免在服务端拼接含插值语法的响应。同时配合CSP(内容安全策略),即便注入发生也限制脚本执行和外部请求能力。CSP中要避免使用unsafe-eval,并谨慎对待unsafe-inline。
最后,建立检测手段。在安全测试中加入模板注入的探测payload,例如在回显位置提交{{7*7}}、{{2+2}},观察渲染结果是否被求值;代码评审时重点检查bypassSecurityTrust系列方法的所有调用点,确认每一处的数据来源都可信。多层防线叠加,才能把Angular表达式注入的风险降到最低。
Angular表达式注入Angular安全漏洞XSS防御修改时间:2026-09-17 00:56:31