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. 歡迎關注公衆號「前端進階課」認真學前端,一塊兒進階。

相關文章
相關標籤/搜索