AngularJS中安全接收HTML输入并防范插值注入漏洞求助
解决AngularJS $interpolate 编译用户模板时的XSS注入问题
我明白你遇到的头疼问题了——本来以为用了$sanitize就能安全处理用户输入的HTML模板,结果输入{{constructor.constructor('alert(1)')()}}居然触发了弹窗,这说明你的安全防护还漏了关键一环。
问题根源
$sanitize的作用是过滤HTML中的危险标签(比如<script>、<iframe>这类),但它管不到Angular插值表达式{{}}里的代码逻辑。$interpolate在编译表达式时,默认会让表达式访问全局上下文(比如浏览器的window对象),而constructor.constructor其实是拿到了全局的Function构造函数,通过它可以动态创建并执行任意代码,这就直接导致了XSS漏洞。
正确的解决方案
要解决这个问题,核心是限制插值表达式能访问的上下文范围,彻底切断它对全局对象或危险原型属性的访问。这里有两种可靠的实现方式:
方式一:使用无原型的上下文对象
创建一个没有原型链的纯净对象,只把业务需要的属性传入,从根源上阻止表达式访问全局和原型属性:
let $interpolate = this.$injector.get('$interpolate'); let $sanitize = this.$injector.get('$sanitize'); let html = $sanitize(toReturnStyles.cellTemplate); // 创建无原型的上下文,避免访问Object.prototype的属性 const safeContext = Object.create(null); // 只复制你允许用户访问的属性,比如params里的name、value等 Object.assign(safeContext, { name: params.name, value: params.value // 这里只放业务必需的属性,不要直接传入整个params }); // 用安全上下文执行插值 el = $interpolate(html)(safeContext);
方式二:用Proxy拦截危险属性访问
如果需要保留原params的结构,又要阻止危险属性的访问,可以用ES6的Proxy来做拦截:
let $interpolate = this.$injector.get('$interpolate'); let $sanitize = this.$injector.get('$sanitize'); let html = $sanitize(toReturnStyles.cellTemplate); // 创建Proxy拦截器,禁止访问危险属性 const safeContext = new Proxy(params, { get(target, prop) { // 拦截constructor、__proto__、prototype等危险属性 const forbiddenProps = ['constructor', '__proto__', 'prototype', 'eval', 'Function']; if (forbiddenProps.includes(prop)) { return undefined; } return target[prop]; } }); // 用拦截后的上下文执行插值 el = $interpolate(html)(safeContext);
额外注意事项
- 永远不要让用户输入的模板包含Angular指令(比如
ng-click、ng-bind),如果必须允许,一定要用$compile配合严格的SCE校验,但这风险更高,尽量避免。 - 只暴露业务必需的属性给插值上下文,遵循最小权限原则,不要把整个
params对象直接传入,减少攻击面。
内容的提问来源于stack exchange,提问作者Ganesh Nemade
相关产品推荐
相关产品推荐

