JSX
Let’s briefly review the basics of JSX first. JSX YesSyntax sugar
<div >hello</div>
go throughbabel
After compilation:
( "div" /* type */, { id: "container" } /* props */, "hello" /* children */ );
The final result is an object.as follows:
{ type: 'div', props: { id: 'container', children: 'hello', }, key: null, ref: null, $$typeof: (''), }
This is a React element object.
We can even write React element objects directly in the code.React can still render our content normally:
render() { return ( <div> {{ $$typeof: (''), props: { dangerouslySetInnerHTML: { __html: '<img src="x" onerror="alert(1)">' }, }, ref: null, type: "div", }} </div> ); }
You can copy this code and run it locally. You can find a pop-up window popup in the browser, andimg
Already inserted into the dom.
here,$$typeof
What is the function of ? Why useSymbol()
As a value?
Before we understand, let's take a brief look.XSS
Attack
XSS Attack
We often need to construct HTML strings and insert them into the DOM, for example:
const messageEl = ("message"); var message = "hello world"; = "<p>" + message + "</p>";
The page is displayed normally. But if we insert some malicious code,for example:
const messageEl = ("message"); var message = '<img src onerror="alert(1)">'; = "<p>" + message + "</p>";
At this time, a pop-up window will pop up on the page, and the content of the pop-up window will be displayed as 1
Therefore, directly using innerHTML to insert text content poses a risk of XSS attacks
How to prevent XSS attacks
To solve a similar XSS attack method, we can use some secure APIs to add text content.for example:
- use
('hello world')
Insert text node. - Or use
textContent
Instead ofinnerHTML
Set text content. - For some special characters, such as
<
、>
, we can escape and convert it to<
as well as>
- For rich text content, we can set a blacklist to filter some attributes, such as
onerror
wait.
React processing of text nodes
React UsecreateTextNode
ortextContent
Set text content.
For the following code:
render() { const { count } = return ( <div onClick={() => ({ count: count + 1})}> {count} </div> ); }
React will be called during renderingsetTextContent
The method isdiv
The node settings content, where, when rendering for the first time, it is set directlydiv
Node'stextContent
, the second or second update rendering, because the first time it is settextContent
,thereforediv
offirstChild
The value exists and is a text node. This text node is updated directlynodeValue
Just
var setTextContent = function (node, text) { if (text) { var firstChild = ; // If the current node node has already set textContent, firstChild is not empty, it is a text node TEXT_NODE if ( firstChild && firstChild === && === TEXT_NODE ) { = text; return; } } // For the first rendering, directly set textContent = text; };
In summary, for ordinary text nodes, since React is added in textContent or createTextNode, there will be no XSS attacks, even if the value of count is'<img src="x" onerror="alert(1)">'
There is no risk of being attacked
dangerouslySetInnerHTML handles rich text nodes
Sometimes we do need to display rich text content, React providesdangerouslySetInnerHTML
Convenient for us to explicitly insert rich text content
render() { return ( <div dangerouslySetInnerHTML={{ __html: '<img src="x" onerror="alert(1)">' }} > </div> ); }
React determines the attribute'skey
Yes or nodangerouslySetInnerHTML
, if so, callsetInnerHTML
The method is directly given to the dominnerHTML
Properties Set Text Content
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) { = html; };
It can be seen that when React processes rich text, it is just a simple setting of DOM.innerHTML
attributes are implemented.
The potential security risks of rich texts are left to the developer to control them.
The role of $$typeof
render() { const { text } = return ( <div> {text} </div> ); }
Assume thistext
It is returned from the backend, and the backend allows the user to store JSON objects.If the user passes in the following object similar to React element:
{ type: "div", props: { dangerouslySetInnerHTML: { __html: '<img src="x" onerror="alert(1)">' }, }, ref: null }
Don't forget as mentioned earlier, we can render normally by inserting React element objects directly in JSX.
In this case,React0.13
When it comes to version, this is a potentialXSS
attack,This vulnerability originated from the server. If the attacker maliciously forges data similar to a React element object and returns it to the front end, React will execute malicious code. But React can take steps to prevent such attacks.
fromReact0.14
Starting with the release, React adds one for each elementSymbol
Logo:
{ $$typeof: (''), props: { id: 'container' }, ref: null, type: "div", }
This works because JSON does not support itSymbol
. Therefore, even if the server has a risky vulnerability and returns a JSON, this JSON will not include('').
, During the Reconcile stage, React will checkelement.$$typeof
Whether the sign is legal. If it is illegal, it will directly report an error. React cannot accept the object as children
The advantage of using () specifically is that Symbols is global between environments such as iframe and worker. Therefore, even under more peculiar conditions, Symbols can pass trusted elements between different applications. Likewise, even if there are multiple React copies on the page, they can still "consent" to valid $$typeof values
If the browser does not support itSymbols
, React uses0xeac7
replace
{ $$typeof: '0xeac7', }
This is the end of this article about how React prevents XSS attacks from being attacked. For more related React $$typeof, please search for my previous articles or continue browsing the related articles below. I hope everyone will support me in the future!