React用户安全的第一道防线

React用户安全的第一道防线是什么?React是怎么预防XSS攻击的?javascript

首先简单介绍下JSX

当你在写 JSX 时,其实你在调用createElement方法。html

React.createElement(
  /* type */ 'marquee',
  /* props */ { bgcolor: '#ffa7c4' },
  /* children */ 'hi'
)
复制代码

createElement 会返回一个对象,咱们称此对象为React的 元素(element),它告诉 React 下一个要渲染什么。你的组件(component)返回一个它们组成的树(tree)。前端

{
  type: 'marquee',
  props: { //... },
  key: null,
  ref: null,
  $$typeof: Symbol.for('react.element'),
}
复制代码

HTML的插入转义

在客户端 UI 库变得广泛且具备基本保护做用以前,应用程序代码一般是先构建 HTML,而后把它插入 DOM 中:java

const messageEl = document.getElementById('message');
messageEl.innerHTML = '<p>' + message.text + '</p>';
复制代码

这样看起来没什么问题,但当你 message.text 的值相似 '<img src onerror="stealYourPassword()">' 时, 你不会但愿别人写的内容在你应用的 HTML 中逐字显示的。react

为何防止此类攻击,你能够用只处理文本的 document.createTextNode() 或者 textContent等安全的 API。你也能够事先将用户输入的内容,用转义符把潜在危险字符( <>等)替换掉。web

尽管如此,这个问题的成本代价很高,且很难作到用户每次输入都记得转换一次。 所以像React等新库会默认进行文本转义:安全

若是 message.text 是一个带有 <img> 或其余标签的恶意字符串,它不会被当成真的 <img> 标签处理,React 会先进行转义而后插入 DOM 里。因此 <img> 标签会以文本的形式展示出来。服务器

在 React 中若是元素要渲染 HTML,那么须要使用 dangerouslySetInnerHTML={{ __html: message.text }}ui

这意味着React彻底不惧注入攻击了吗?不,HTML 和 DOM 暴露了大量攻击点,对 React 或者其余 UI 库来讲,要减轻伤害太难或进展缓慢。大部分存在的攻击方向涉及到属性,例如,若是你渲染 <a href={user.website},要提防用户的网址是 'javascript: stealYourPassword()'。 像 <div {...userData}> 写法几乎不受用户输入影响,但也有危险。spa

不过,转义文本这第一道防线能够拦下许多潜在攻击,知道这样的代码是安全的就够了吗?不必定,因此咱们须要$$typeof

关于 $$typeof

若是你用过 React,对 typepropskey、 和 ref 应该熟悉。 但你不必定知道 $$typeof

若是你的服务器有容许用户存储任意 JSON 对象的漏洞,而前端须要一个字符串,这可能会发生一个问题:

// 服务端容许用户存储 JSON
let expectedTextButGotJSON = {
  type: 'div',
  props: {
    dangerouslySetInnerHTML: {
      __html: '/* 把你想的放在这里 */'
    },
  },
  // ...
};
let message = { text: expectedTextButGotJSON };

// React 0.13 中有风险
<p>
  {message.text}
</p>
复制代码

在这个例子中,React 0.13 很容易受到 XSS 攻击。虽然 这个攻击是服务端存在漏洞致使的。不过,从 React 0.14 开始,这个问题修复了。

React 0.14 修复手段是在虚拟DOM中添加 $$typeof,使用 Symbol 标记每一个 React 元素(element):

Symbol类型是很是重要的,由于JSON不支持 Symbol 类型。 因此即便服务器存在用JSON做为文本返回安全漏洞,JSON 里也不包含 Symbol.for('react.element')。React 会检测 element.$$typeof,若是元素丢失或者无效,会拒绝处理该元素。

特地用 Symbol.for() 的好处是 Symbols 通用于 iframes 和 workers 等环境中。所以不管在多奇怪的条件下,这方案也不会影响到应用不一样部分传递可信的元素。一样,即便页面上有不少个 React 副本,它们也 「接受」 有效的 $$typeof 值。

为何是这个数字?由于 0xeac7 看起来有点像 「React」。

最后

  1. 以为有用点个赞呗
  2. 欢迎关注公众号「前端进阶课」认真学前端,一块儿进阶。

相关文章
相关标签/搜索