Chrome团队发布Trusted Types API,致力于JavaScript DOM安全

防范JavaScript中的DOM XSS安全漏洞

DOM XSS攻击是指黑客的恶意代码因修改浏览器中的DOM环境而被执行。

DOM XSS注入可以通过各种方式发生。例如:

  • 接受要加载的代码的URL的元素属性的设置器,如HTMLScriptElement.src
  • 接受要执行的代码的元素属性设置器,如HTMLScriptElement.text
  • 直接执行代码的函数,如eval

因此,需要采取必要的措施避免这些问题。

Trusted Types API

与基于服务器端XSS漏洞相比,基于DOM的XSS漏洞不断增加,所以Google Chrome的团队推出了一个名为Trusted Types API的API,来控制这些DOM XSS的安全漏洞。

这是因为DOM XSS很容易引入,但更难检测。

Trusted Types API如何防止DOM XSS攻击?

Trusted Types API能够从根源上解决XSS问题。

DOM API在默认情况下是不安全的。例如:

eval('foo()');
document.createElement('div').innerHTML = '<foo>';
document.createElement('a').setAttribute('onclick'', 'foo()');

很容易向这些注入恶意脚本或恶意HTML。

为了避免这种情况,该API能够将内容安全策略(CSP)的HTTP响应头设置为Content-Security-Policy: trusted-types \*,以只利用可信的类型。这将使开发人员能够阻止危险的注入,使他们在默认情况下是安全的。

可以按以下方式启用:

Content-Security-Policy: trusted-types;
Content-Security-Policy: trusted-types 'none';
Content-Security-Policy: trusted-types <policyName>;
Content-Security-Policy: trusted-types <policyName> <policyName> 'allow-duplicates';

Trusted-types指令指示浏览器建立不可欺骗的、类型化的值,以代替字符串传递给DOM XSS。

这里的主要想法是将对象传递给DOM,而不是字符串。DOM支持对象的传递。

elem.innerHTML = { toString: function() { return 'Hello World' }};
elem.innerHTML // returns Hello World

Trusted Types API推荐的是传递类型化的对象而不是普通的JS对象。

这将使DOM汇拒绝字符串,只接受匹配的类型。

在实践中使用可信的类型API

如果打算在你的项目中使用Trusted Types API,首先,需要找出不符合信任类型的地方。

检查是否有违反信任类型的情况

将以下HTTP响应头添加到需要检查违规的文件中。

Content-Security-Policy-Report-Only: require-trusted-types-for 'script'; report-uri //mysite.com/cspViolations

然后,所有的违规行为将被报告给//mysite.com/cspViolations 。这不会妨碍网站的任何功能。

受信任的类型违规报告

当检测到信任类型违规时,它将被发送到一个使用report-uri配置的报告。例如,如果你向innerHTML传递了一个字符串,将生成类似于下面的报告。

{
  "csp-report": {
    "document-uri": "https://mysite.com",
    "violated-directive": "require-trusted-types-for",
    "disposition": "report",
    "blocked-uri": "trusted-types-sink",
    "line-number": 20,
    "column-number": 12,
    "source-file": "https://mysite.com/dashboard.js",
    "status-code": 0,
    "script-sample": "Element innerHTML <img src=x"
  }
}

这将帮助确定哪些文件中的哪行代码会导致你的网站出现DOM XSS漏洞。

解决违规问题

可以用一些方法来消除可信类型的违规。

1.重写违规的代码

例如,如果你有一段代码,如el.innerHTML = '';,这可以重新写成如下。

el.textContent = '';
const img = document.createElement('img');
img.src = 'xyz.jpg';
el.appendChild(img);

这就避免了直接给innerHTML分配一个字符串。

2.使用一个库

DOMPurify这样的库可以用来净化HTML,它返回的HTML被包裹在一个TrustedHTML对象中。这就不允许浏览器产生违规。

3.创建一个可信的类型策略

你可以自己创建一个受信任的类型对象,而不是使用一个库或删除有漏洞的代码。可信的类型策略对其输入强制执行安全规则。

  • 第1步 - 创建一个策略
if (window.trustedTypes && trustedTypes.createPolicy) {
  const escapeHTMLPolicy = trustedTypes.createPolicy('escapePolicy',    
  {
    createHTML: string => string.replace(/\</g, '&lt;')
  });
}

这个规则将省略<字符,以防止创建新的HTML元素。createPolicy()返回一个策略对象,将返回值包裹在一个正确的类型中;在这个例子中是TrustedHTML 。

  • 第2步 - 使用策略
const escaped = escapeHTMLPolicy.createHTML('<img src=x onerror=alert(1)>');
console.log(escaped instanceof TrustedHTML);  // true
el.innerHTML = escaped;  // '&lt;img src=x onerror=alert(1)>'

如果你不能改变代码(例如,从CDN采购第三方库),你可以使用一个默认的策略。

if (window.trustedTypes && trustedTypes.createPolicy) {
  trustedTypes.createPolicy('default', {
    createHTML: (string, sink) => DOMPurify.sanitize(string, {RETURN_TRUSTED_TYPE: true})
  });
}

这是一个政策和净化库的结合。

一旦你解决了所有的违规问题,你就可以在你的代码中强制执行信任类型。这是按以下方式进行的。

Content-Security-Policy: require-trusted-types-for 'script'; report-uri //mysite.com/cspViolations

你可能注意到,与以前的实现相比,这里没有包括-Report-Only后缀。

我们只看了解决HTML违规的问题。然而,Trusted Types API有能力在以下方面检测违规行为并执行规则:

  • HTML
  • 脚本
  • 脚本URL
  • 脚本URL

强制执行受信任类型的好处

  • 减少你网站的攻击面 - 应用程序是安全的
  • 在编译时和运行时进行安全验证
  • 向后兼容--能够使用可信的类型来代替字符串
  • 补充其他安全解决方案,如用于服务器端XSS的CSP

浏览器的兼容性

不幸的是,Firefox和Internet Explorer并不支持Trusted Types。所以如果你是Chrome浏览器用户就很幸运了。

原文链接:blog.bitsrc.io/trusted-typ…