詳解React?如何防止?XSS?攻擊論$$typeof?的作用
JSX
先來(lái)簡(jiǎn)單復(fù)習(xí)一下 JSX 的基礎(chǔ)知識(shí)。JSX 是React.createElement的語(yǔ)法糖
<div id="container">hello</div>
經(jīng)過(guò) babel 編譯后:
React.createElement(
"div" /* type */,
{ id: "container" } /* props */,
"hello" /* children */
);React.createElement最終返回的結(jié)果就是一個(gè)對(duì)象,如下:
{
type: 'div',
props: {
id: 'container',
children: 'hello',
},
key: null,
ref: null,
$$typeof: Symbol.for('react.element'),
}這就是一個(gè) React element 對(duì)象。
我們甚至可以在代碼中直接寫(xiě) React element 對(duì)象,React 照樣能正常渲染我們的內(nèi)容:
render() {
return (
<div>
{{
$$typeof: Symbol.for('react.element'),
props: {
dangerouslySetInnerHTML: {
__html: '<img src="x" onerror="alert(1)">'
},
},
ref: null,
type: "div",
}}
</div>
);
}可以復(fù)制這段代碼本地運(yùn)行一下,可以發(fā)現(xiàn)瀏覽器彈出一個(gè)彈窗,并且img已經(jīng)插入了 dom 中。
這里,$$typeof 的作用是啥?為什么使用 Symbol() 作為值?
在了解之前,我們先來(lái)簡(jiǎn)單看下 XSS 攻擊
XSS 攻擊
我們經(jīng)常需要構(gòu)造 HTML 字符串并插入到 DOM 中,比如:
const messageEl = document.getElementById("message");
var message = "hello world";
messageEl.innerHTML = "<p>" + message + "</p>";頁(yè)面正常顯示。但是如果我們插入一些惡意代碼,比如:
const messageEl = document.getElementById("message");
var message = '<img src onerror="alert(1)">';
messageEl.innerHTML = "<p>" + message + "</p>";此時(shí)頁(yè)面就會(huì)彈出一個(gè)彈窗,彈窗內(nèi)容顯示為 1
因此,直接使用 innerHTML 插入文本內(nèi)容,存在 XSS 攻擊的風(fēng)險(xiǎn)
防止 XSS 攻擊的方法
為了解決類(lèi)似的 XSS 攻擊方法,我們可以使用一些安全的 API 添加文本內(nèi)容,比如:
- 使用
document.createTextNode('hello world')插入文本節(jié)點(diǎn)。 - 或者使用
textContent而不是innerHTML設(shè)置文本內(nèi)容。 - 對(duì)于一些特殊字符,比如
<、>,我們可以進(jìn)行轉(zhuǎn)義,將其轉(zhuǎn)換為<以及> - 對(duì)于富文本內(nèi)容,我們可以設(shè)置黑名單,過(guò)濾一些屬性,比如
onerror等。
React 對(duì)于文本節(jié)點(diǎn)的處理
React 使用 createTextNode 或者 textContent 設(shè)置文本內(nèi)容。
對(duì)于下面的代碼:
render() {
const { count } = this.state
return (
<div onClick={() => this.setState({ count: count + 1})}>
{count}
</div>
);
}React 在渲染過(guò)程中會(huì)調(diào)用setTextContent方法為div節(jié)點(diǎn)設(shè)置內(nèi)容,其中,第一次渲染時(shí),直接設(shè)置div節(jié)點(diǎn)的textContent,第二次或者第二次以后的更新渲染,由于第一次設(shè)置了textContent,因此div的firstChild值存在,是個(gè)文本節(jié)點(diǎn)。此時(shí)直接更新這個(gè)文本節(jié)點(diǎn)的nodeValue即可
var setTextContent = function (node, text) {
if (text) {
var firstChild = node.firstChild;
// 如果當(dāng)前node節(jié)點(diǎn)已經(jīng)設(shè)置過(guò)textContent,則firstChild不為空,是個(gè)文本節(jié)點(diǎn)TEXT_NODE
if (
firstChild &&
firstChild === node.lastChild &&
firstChild.nodeType === TEXT_NODE
) {
firstChild.nodeValue = text;
return;
}
}
// 第一次渲染,直接設(shè)置textContent
node.textContent = text;
};綜上,對(duì)于普通的文本節(jié)點(diǎn)來(lái)說(shuō),由于 React 是采用 textContent 或者 createTextNode 的方式添加的,因此是不會(huì)存在 XSS 攻擊的,即使上面示例中,count 的值為 '<img src="x" onerror="alert(1)">'也不會(huì)有被攻擊的風(fēng)險(xiǎn)
dangerouslySetInnerHTML 處理富文本節(jié)點(diǎn)
有時(shí)候我們確實(shí)需要顯示富文本的內(nèi)容,React 提供了dangerouslySetInnerHTML方便我們顯式的插入富文本內(nèi)容
render() {
return (
<div
id="dangerous"
dangerouslySetInnerHTML={{ __html: '<img src="x" onerror="alert(1)">' }}
>
</div>
);
}React 在為 DOM 更新屬性時(shí),會(huì)判斷屬性的key是不是dangerouslySetInnerHTML,如果是的話,調(diào)用setInnerHTML 方法直接給 dom 的innerHTML屬性設(shè)置文本內(nèi)容
function setInitialDOMProperties(
tag,
domElement,
rootContainerElement,
nextProps
) {
for (var propKey in nextProps) {
if (propKey === "dangerouslySetInnerHTML") {
var nextHtml = nextProp ? nextProp.__html : undefined;
if (nextHtml != null) {
setInnerHTML(domElement, nextHtml);
}
}
}
}
var setInnerHTML = function (node, html) {
node.innerHTML = html;
};可以看出,React 在處理富文本時(shí),也僅僅是簡(jiǎn)單的設(shè)置 DOM 的innerHTML屬性來(lái)實(shí)現(xiàn)的。
對(duì)于富文本潛在的安全風(fēng)險(xiǎn),交由開(kāi)發(fā)者自行把控。
$$typeof 的作用
render() {
const { text } = this.state
return (
<div>
{text}
</div>
);
}假設(shè)這個(gè)text是從后端返回來(lái)的,同時(shí)后端允許用戶(hù)存儲(chǔ) JSON 對(duì)象,如果用戶(hù)傳入下面這樣的一個(gè)類(lèi)似 React element 的對(duì)象:
{
type: "div",
props: {
dangerouslySetInnerHTML: {
__html: '<img src="x" onerror="alert(1)">'
},
},
ref: null
}別忘了前面說(shuō)過(guò),我們?cè)?JSX 中直接插入 React element 對(duì)象也是能夠正常渲染的。
在這種情況下,在React0.13版本時(shí),這是一個(gè)潛在的XSS攻擊,這個(gè)漏洞源于服務(wù)端。如果攻擊者惡意偽造一個(gè)類(lèi)似 React element 對(duì)象的數(shù)據(jù)返回給前端,React 就會(huì)執(zhí)行惡意代碼。但是 React 可以采取措施預(yù)防這種攻擊。
從React0.14版本開(kāi)始,React 為每個(gè) element 都添加了一個(gè)Symbol標(biāo)志:
{
$$typeof: Symbol.for('react.element'),
props: {
id: 'container'
},
ref: null,
type: "div",
}這個(gè)行得通,是因?yàn)?JSON 不支持Symbol。因此即使是服務(wù)端有風(fēng)險(xiǎn)漏洞并且返回一個(gè) JSON,這個(gè) JSON 也不會(huì)包含Symbol.for('react.element').,在 Reconcile 階段,React 會(huì)檢查element.$$typeof標(biāo)志是否合法。不合法的話直接報(bào)錯(cuò),React 不能接受對(duì)象作為 children
專(zhuān)門(mén)使用 Symbol.for() 的好處是, Symbols 在 iframe 和 worker 等環(huán)境之間是全局的。因此,即使在更奇特的條件下,Symbols 也能在不同的應(yīng)用程序之間傳遞受信任的元素。同樣,即使頁(yè)面上有多個(gè) React 副本,它們?nèi)匀豢梢?ldquo;同意”有效的 $$typeof 值
如果瀏覽器不支持Symbols,React 使用0xeac7代替
{
$$typeof: '0xeac7',
}到此這篇關(guān)于詳解React 如何防止 XSS 攻擊論$$typeof 的作用的文章就介紹到這了,更多相關(guān)React $$typeof 內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
詳解如何在React單頁(yè)面應(yīng)用中捕獲錯(cuò)誤
在當(dāng)前的Web開(kāi)發(fā)中,使用React構(gòu)建單頁(yè)面應(yīng)用(SPA)已經(jīng)成為一種常見(jiàn)的做法,然而,當(dāng)應(yīng)用程序遇到錯(cuò)誤時(shí),有可能會(huì)導(dǎo)致整個(gè)頁(yè)面崩潰,給用戶(hù)帶來(lái)不好的體驗(yàn),本文將介紹如何在React單頁(yè)面應(yīng)用中捕獲錯(cuò)誤,以防止整個(gè)頁(yè)面的崩潰,需要的朋友可以參考下2023-09-09
詳解如何封裝和使用一個(gè)React鑒權(quán)組件
JavaScript?和?React?提供了靈活的事件處理機(jī)制,特別是通過(guò)利用事件的捕獲階段來(lái)阻止事件傳播可以實(shí)現(xiàn)精細(xì)的權(quán)限控制,本文將詳細(xì)介紹這一技術(shù)的應(yīng)用,并通過(guò)實(shí)踐案例展示如何封裝和使用一個(gè)?React?鑒權(quán)組件,需要的可以參考下2024-03-03
每天一個(gè)hooks學(xué)習(xí)之useUpdateEffect
這篇文章主要為大家介紹了每天一個(gè)hooks學(xué)習(xí)之useUpdateEffect使用示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-05-05
詳解react使用react-bootstrap當(dāng)輪子造車(chē)
本篇文章主要介紹了詳解react使用react-bootstrap當(dāng)輪子造車(chē),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下。2017-08-08
react中setState的執(zhí)行機(jī)制詳解
setState() 的執(zhí)行機(jī)制包括狀態(tài)合并、批量更新、異步更新、虛擬 DOM 比較和渲染組件等步驟,這樣可以提高性能并優(yōu)化渲染過(guò)程,這篇文章主要介紹了react中的setState的執(zhí)行機(jī)制,需要的朋友可以參考下2023-10-10
React組件化學(xué)習(xí)入門(mén)教程講解
React是現(xiàn)在前端使用頻率最高的三大框架之一,React率先提出虛擬DOM的思想和實(shí)現(xiàn),使其保持有良好的性能。本篇文章將對(duì)React組件化的入門(mén)學(xué)習(xí)進(jìn)行講解,同時(shí)針對(duì)模塊化的思想進(jìn)行概述,為接下來(lái)組件化開(kāi)發(fā)的文章進(jìn)行知識(shí)儲(chǔ)備2022-09-09

