官术网_书友最值得收藏!

  • React Cookbook
  • Carlos Santana Roldan
  • 1330字
  • 2021-07-16 17:49:42

How to do it...

We will now see how to create our XSS component:

  1. Create an XSS component:
  import React, { Component } from 'react';

// Let's suppose this response is coming from a service and have
// some XSS attacks in the content...

const response = [
{
id: 1,
title: 'My blog post 1...',
content: '<p>This is <strong>HTML</strong> code</p>'
},
{
id: 2,
title: 'My blog post 2...',
content: `<p>Alert: <script>alert(1);</script></p>`
},
{
id: 3,
title: 'My blog post 3...',
content: `
<p>
<img
onmouseover="alert('This site is not secure');"
src="attack.jpg" />
</p>
`
}
];

// Let's suppose this is our initialState of Redux
// which is injected to the DOM...

const initialState = JSON.stringify(response);

class Xss extends Component {
render() {
// Parsing the JSON string to an actual object...
const posts = JSON.parse(initialState);

// Rendering our posts...
return (
<div className="Xss">
{posts.map((post, key) => (
<div key={key}>
<h2>{post.title}</h2>

<p>{post.content}</p>
</div>
))}
</div>
);
}
}

export default Xss;
File: src/components/Xss/Xss.js
  1. If you render this component, you will see something like this:
  1. As you can see, by default, React prevents us from injecting HTML code directly into our components. It is rendering the HTML as a string. This is good, but sometimes we need to insert HTML code in our components.
  2. Implementing dangerouslySetInnerHTML: This prop probably scares you a little bit (maybe because it explicitly says the word danger!). I'm going to show you that this prop is not too bad if we know how to use it securely. Let's modify our previous code, and we are going to add this prop to see how the HTML is rendering it now:
import React, { Component } from 'react';
// Let's suppose this response is coming from a service and have
// some XSS attacks in the content...

const response = [
{
id: 1,
title: 'My blog post 1...',
content: '<p>This is <strong>HTML</strong> code</p>'
},
{
id: 2,
title: 'My blog post 2...',
content: `<p>Alert: <script>alert(1);</script></p>`
},
{
id: 3,
title: 'My blog post 3...',
content: `
<p>
<img onmouseover="alert('This site is not secure');"
src="attack.jpg" />
</p>
`
}
];

// Let's suppose this is our initialState of Redux
// which is injected to the DOM...

const initialState = JSON.stringify(response);

class Xss extends Component {
render() {
// Parsing the JSON string to an actual object...
const posts = JSON.parse(initialState);

// Rendering our posts...
return (
<div className="Xss">
{posts.map((post, key) => (
<div key={key}>
<h2>{post.title}</h2>
<p><strong>Secure Code:</strong></p>
<p>{post.content}</p>
<p><strong>Insecure Code:</strong></p>
<p
dangerouslySetInnerHTML={{ __html: post.content }}
/>
</div>
))}
</div>
);
}
}

export default Xss;
File: src/components/Xss/Xss.js
  1. Our site should now look like this:
  1. It is interesting, probably you thought that the content of "My blog post 2" will fire an alert in the browser but does not. If we inspect the code the alert script is there.
  1. Even if we use dangerouslySetInnerHTML, React protects us from malicious scripts injections, but it is not secure enough for us to relax on the security aspect of our site. Now let's see the issue with My blog post 3 content. The code <img onmouseover="alert('This site is not secure');" src="attack.jpg" /> is not directly using a <script> tag to inject a malicious code, but is using an img tag with an event (onmouseover). So, if you were happy about React's protection, we can see that this XSS attack will be executed if we move the mouse over the image:
  1. Removing XSS attacks: This is kind of scary, right? But as I said at the beginning of this recipe, there is a secure way to use dangerouslySetInnerHTML and, yes, as you may be thinking right now, we need to clean our code of malicious scripts before we render it with dangerouslySetInnerHTML. The next script will take care of removing <script> tags and events from tags, but of course, you can modify this depending on the security level you want to have:
  import React, { Component } from 'react';

// Let's suppose this response is coming from a service and have
// some XSS attacks in the content...

const response = [
{
id: 1,
title: 'My blog post 1...',
content: '<p>This is <strong>HTML</strong> code</p>'
},
{
id: 2,
title: 'My blog post 2...',
content: `<p>Alert: <script>alert(1);</script></p>`
},
{
id: 3,
title: 'My blog post 3...',
content: `
<p>
<img onmouseover="alert('This site is not secure');"
src="attack.jpg" />
</p>
`
}
];

// Let's suppose this is our initialState of Redux
// which is injected to the DOM...

const initialState = JSON.stringify(response);

const removeXSSAttacks = html => {
const SCRIPT_REGEX = /<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi;

// Removing the <script> tags
while (SCRIPT_REGEX.test(html)) {
html = html.replace(SCRIPT_REGEX, '');
}

// Removing all events from tags...
html = html.replace(/ on\w+="[^"]*"/g, '');

return {
__html: html
}
};

class Xss extends Component {
render() {
// Parsing the JSON string to an actual object...
const posts = JSON.parse(initialState);

// Rendering our posts...
return (
<div className="Xss">
{posts.map((post, key) => (
<div key={key}>
<h2>{post.title}</h2>
<p><strong>Secure Code:</strong></p>
<p>{post.content}</p>
<p><strong>Insecure Code:</strong></p>
<p
dangerouslySetInnerHTML=
{removeXSSAttacks(post.content)}
/>
</div>
))}
</div>
);
}
}

export default Xss;
File: src/components/Xss/Xss.js

  1. If we look at the code now, we will see that now our render is more secure:
  1. The problem with JSON.stringify: So far, we have learned how to inject HTML code into a React component with dangerouslySetInnerHTML, but there is another potential security issue using JSON.stringify. If we have an XSS attack (<script> tag inside the content) in our response and then we use JSON.stringify to convert the object to a string, the HTML tags are not encoded. That means that if we inject the string into our HTML (like Redux does with the initial state), we will have a potential security issue. The output of JSON.stringify(response) is this:
  [
{"id":1,"title":"My blog post 1...","content":"<p>This is <strong>HTML</strong> code</p>"},
{"id":2,"title":"My blog post 2...","content":"<p>Alert: <script>alert(1);</script></p>"},
{"id":3,"title":"My blog post 3...","content":"<p><img onmouseover=\"alert('This site is not secure');\" src=\"attack.jpg\" /></p>"}
]
  1. As you can see, all the HTML is exposed without any encoding characters, and that is a problem. But how we can fix this? We need to install a package called serialize-javascript:
 npm install serialize-javascript

  1. Instead of using JSON.stringify, we need to serialize the code like this:
  import serialize from 'serialize-javascript';

// Let's suppose this response is coming from a service and have
// some XSS attacks in the content...

const response = [
{
id: 1,
title: 'My blog post 1...',
content: '<p>This is <strong>HTML</strong> code</p>'
},
{
id: 2,
title: 'My blog post 2...',
content: `<p>Alert: <script>alert(1);</script></p>`
},
{
id: 3,
title: 'My blog post 3...',
content: `<p><img onmouseover="alert('This site is not
secure');" src="attack.jpg" /></p>`
}
];

// Let's suppose this is our initialState of Redux which is
// injected to the DOM...

const initialState = serialize(response);
console.log(initialState);
  1. The output of the console is as follows:
  [
{"id":1,"title":"My blog post 1...","content":"\u003Cp\u003EThis is \u003Cstrong\u003EHTML\u003C\u002Fstrong\u003E code\u003C\u002Fp\u003E"},
{"id":2,"title":"My blog post 2...","content":"\u003Cp\u003EAlert: \u003Cscript\u003Ealert(1);\u003C\u002Fscript\u003E\u003C\u002Fp\u003E"},
{"id":3,"title":"My blog post 3...","content":"\u003Cp\u003E\u003Cimg onmouseover=\"alert('This site is not secure');\" src=\"attack.jpg\" \u002F\u003E\u003C\u002Fp\u003E"}
]

  1. Now that we have our code with HTML entities (encoded) instead of directly having HTML tags, and the good news is that we can use JSON.parse to convert this string again into our original object. Our component should look like this:
  import React, { Component } from 'react';
import serialize from 'serialize-javascript';

// Let's suppose this response is coming from a service and have
// some XSS attacks in the content...

const response = [
{
id: 1,
title: 'My blog post 1...',
content: '<p>This is <strong>HTML</strong> code</p>'
},
{
id: 2,
title: 'My blog post 2...',
content: `<p>Alert: <script>alert(1);</script></p>`
},
{
id: 3,
title: 'My blog post 3...',
content: `<p><img onmouseover="alert('This site is not secure');"
src="attack.jpg" /></p>`
}
];

// Let's suppose this is our initialState of Redux which is
// injected to the DOM...

const secureInitialState = serialize(response);
// const insecureInitialState = JSON.stringify(response);

console.log(secureInitialState);

const removeXSSAttacks = html => {
const SCRIPT_REGEX = /<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi;

// Removing the <script> tags
while (SCRIPT_REGEX.test(html)) {
html = html.replace(SCRIPT_REGEX, '');
}

// Removing all events from tags...
html = html.replace(/ on\w+="[^"]*"/g, '');

return {
__html: html
}
};

class Xss extends Component {
render() {
// Parsing the JSON string to an actual object...
const posts = JSON.parse(secureInitialState);

// Rendering our posts...
return (
<div className="Xss">
{posts.map((post, key) => (
<div key={key}>
<h2>{post.title}</h2>
<p><strong>Secure Code:</strong></p>
<p>{post.content}</p>
<p><strong>Insecure Code:</strong></p>
<p
dangerouslySetInnerHTML={removeXSSAttacks(post.content)}
/>
</div>
))}
</div>
);
}
}

export default Xss;
File: src/components/Xss/Xss.js
主站蜘蛛池模板: 子洲县| 徐闻县| 九寨沟县| 斗六市| 晋中市| 阜城县| 兰州市| 海丰县| 达州市| 个旧市| 鲁甸县| 海城市| 枞阳县| 丰台区| 盘山县| 广河县| 安龙县| 连山| 离岛区| 福安市| 江源县| 临沂市| 山阳县| 延寿县| 博湖县| 晋宁县| 钦州市| 扎囊县| 浦东新区| 康乐县| 长宁区| 德化县| 大新县| 江达县| 新宾| 河北省| 亳州市| 成都市| 增城市| 炎陵县| 富阳市|