Web Integration Patterns in the Era of HTML5

Post on 10-May-2015

1.409 views 1 download

Tags:

description

Presentation given at OWASP BeNeLux November 2012 and GeekMeet Stockholm January 2013. Covers secure and robust integration patterns for the web using cross origin resource sharing (CORS), sandboxed iframes, and the postMessage API.

Transcript of Web Integration Patterns in the Era of HTML5

Web Integration Patterns In the Era of HTML5

@johnwilander atOWASP BeNeLux 2012, Leuven, BelgiumGeekMeet Stockholm, Sweden, 2013

@johnwilander

Part IThe Historyof Web Integration

@johnwilander

Remember when we had pages?

@johnwilander

… maybe a persistent menu system?

Menu

@johnwilander

Menu

… and 3rd party popus?

@johnwilander

You still see such sites.

@johnwilander

But product owners don’t want them

anymore.

@johnwilander

Menu

So we started loading things when we needed them, using Ajax and DOM manipulation with JavaScript.

@johnwilander

Menu

@johnwilander

Menu

The problem was that Ajax was only allowed to the same origin as the page. Ergo, no third party integration.

@johnwilander

But the same-origin policy didn’t apply to all

web elements.

@johnwilander

<form action="http://3rdparty.net"></form>

<img src="http://3rdparty.net">

<script src="http://3rdparty.net"></script>

@johnwilander

<form action="http://3rdparty.net"></form>

<img src="http://3rdparty.net">

<script src="http://3rdparty.net"></script>

The form tag actually had cross-domain intentions and is used for integration with e.g. payment providers such as PayPal. You post a purchase form from maindomain.com and enter the checkout process at paypal.com.

@johnwilander

<form action="http://3rdparty.net"></form>

<img src="http://3rdparty.net">

<script src="http://3rdparty.net"></script>The img tag has been used heavily for cross-origin web tracking such as Google Analytics and Omniture.

@johnwilander

Image srchttp://www.google-analytics.com/__utm.gif?utmwv=5.3.8&utms=1&utmn=1854976096&utmhn=appsandsecurity.blogspot.se&utmcs=UTF-8&utmsr=1920x1200&utmvp=1268x500&utmsc=24-bit&utmul=sv&utmje=1&utmfl=11.5%20r31&utmdt=Apps%20and%20Security&utmhid=2120874108&utmr=-&utmp=%2F&utmac=UA-6984098-5&utmcc=__utma%3D60396157.1187947367.1352854596.1353863861.1353882126.6%3B%2B__utmz%3D60396157.1352933082.2.2.utmcsr%3Dgoogle%7Cutmccn%3D(organic)%7Cutmcmd%3Dorganic%7Cutmctr%3D(not%2520provided)%3B&utmu=q~

Query string parametersutmwv:5.3.8utms:1utmn:1854976096utmhn:appsandsecurity.blogspot.seutmcs:UTF-8utmsr:1920x1200utmvp:1268x500utmsc:24-bitutmul:svutmje:1utmfl:11.5 r31utmdt:Apps and Securityutmhid:2120874108utmr:-utmp:/utmac:UA-6984098-5utmcc:__utma=60396157.1187947367.1352854596.1353863861.1353882126.6;+__utmz=60396157.1352933082.2.2.utmcsr=google|utmccn=(organic)|utmcmd=organic|utmctr=(not%20provided);utmu:q~

@johnwilander

<form action="http://3rdparty.net"></form>

<img src="http://3rdparty.net">

<script src="http://3rdparty.net"></script>

The script tag became the workhorse for Ajax-like cross-origin sharing by means of the popular jsonp hack.

@johnwilander

Jsonp =Json with Padding

(should be ”Json, Please?”)

@johnwilander

maindomain.com

3rdparty.net

<html>

<script> function receive(data) { var json = JSON.parse(data); ... };</script>

</html>

Jsonp

@johnwilander

maindomain.com

3rdparty.net

<html>

<script> function receive(data) { var json = JSON.parse(data); ... };</script>

</html>

Inserts a new script element toretrieve data cross-domain

Jsonp

@johnwilander

maindomain.com

3rdparty.net

<html>

<script> function receive(data) { var json = JSON.parse(data); ... };</script>

<script src=”3rdparty.net/getData?cb=receive”></script>

</html>

Inserts a new script element toretrieve data cross-domain.

Jsonp

@johnwilander

maindomain.com

3rdparty.net

<html>

<script> function receive(data) { var json = JSON.parse(data); ... };</script>

<script src=”3rdparty.net/getData?cb=receive”></script>

</html>

Jsonp

@johnwilander

maindomain.com

3rdparty.net

<html>

<script> function receive(data) { var json = JSON.parse(data); ... };</script>

<script src=”3rdparty.net/getData?cb=receive”></script>

</html>

The third party domain ”pads” theresponse data with a call to the given callback function.

Jsonp

@johnwilander

maindomain.com

3rdparty.net

<html>

<script> function receive(data) { var json = JSON.parse(data); ... };</script>

<script src=”3rdparty.net/getData?cb=receive”></script>

</html>

receive({ ”prop1”: ”data”, ”prop2”: ”data”, ”prop3”: ”data”, ”prop4”: ”data”});

Jsonp

@johnwilander

maindomain.com

3rdparty.net

<html>

<script> function receive(data) { var json = JSON.parse(data); ... };</script>

</html>

{ ”prop1”: ”data”, ”prop2”: ”data”, ”prop3”: ”data”, ”prop4”: ”data”}

receive({ ”prop1”: ”data”, ”prop2”: ”data”, ”prop3”: ”data”, ”prop4”: ”data”});

Jsonp

@johnwilander

maindomain.com

3rdparty.net

<html>

<script> function receive(data) { var json = JSON.parse(data); ... };</script>

</html>

{ ”prop1”: ”data”, ”prop2”: ”data”, ”prop3”: ”data”, ”prop4”: ”data”}

receive({ ”prop1”: ”data”, ”prop2”: ”data”, ”prop3”: ”data”, ”prop4”: ”data”});

Jsonp

Data is fetched asynchronously, cross-domain.

@johnwilander

jQuery's ”crossDomain” Ajax is Jsonp

http://api.jquery.com/jQuery.ajax/

@johnwilander

Jsonp Is Inherently Dangerous

Benign server response: givenCallback({”prop1”: ”val1”});

Compromised server response: givenCallback({”prop1”: ”val1”}); evilCode(); moreEvilCode();

@johnwilander

Jsonp Is Inherently Dangerous

Benign server response: givenCallback({”prop1”: ”val1”});

Compromised server response: givenCallback({”prop1”: ”val1”}); evilCode(); moreEvilCode();

Note that a client doing jsonp calls has nochance to see/filter the reponse before it’s executed.The client has to blindly trust whatever code is returned.

@johnwilander

Jsonp and CSRF

Also, all relevant cookies for the Jsonp service are sent with the request opening up for non-blind CSRF. Non-blind as in the attacker getting the response in full.

@johnwilander

Demo jsonp

@johnwilander

Then there is the document.domain

trick.

@johnwilander

main.1-liner.org

other.1-liner.org

@johnwilander

main.1-liner.org

other.1-liner.org

@johnwilander

main.1-liner.org

other.1-liner.org

@johnwilander

main.1-liner.org

other.1-liner.org

Same-origin policyprohibits evendifferent subdomainsfrom interacting

@johnwilander

main.1-liner.org

document.domain = ”1-liner.org”;

other.1-liner.org

document.domain = ”1-liner.org”;

@johnwilander

main.1-liner.org

document.domain = ”1-liner.org”;

other.1-liner.org

document.domain = ”1-liner.org”;

@johnwilander

main.1-liner.org

document.domain = ”1-liner.org”;

main.1-liner.org

But …

@johnwilander

Demo document.domain

@johnwilander

In summary, web integration used to be

all-or-nothing.

@johnwilander

Part 2New Integration Technologies

@johnwilander

CORS var req = new XMLHttpRequest(); req.open(method, crossDomainUrl); req.send();

Sandboxed iframes <iframe src="http://3rdparty.net" sandbox="allow-scripts"></iframe>

postMessage API otherFrameOrWindow.postMessage( ’{"action": "purchase", "item": 34443}’, "http://3rdparty.net");

@johnwilander

Cross-Origin Resource Sharing, CORS

10+Partial supportin IE8 and IE9

15+

5.1+

22+

12+

3.2+

2.1+

http://caniuse.com/#search=cors

@johnwilander

CORS is basically cross-origin Ajax

@johnwilander

CORS

• The server has to authorize requests in response headers: Access-Control-Allow-Origin allowed.domain.com

• HTTP GET and POST are like normal Ajax

• Other HTTP methods or GET and POST with custom headers require a preflight

• The client has to explicitly send cookies: xhr.withCredentials = true;

@johnwilander

CORS-Unaware Server

Let's see what 2012 looks like if we are still running a …

@johnwilander

CORS-Unaware ServerClient Server

Ajax GET

Looks likea normal GET.Has a reliable Origin header.

If no authori-zation logic is present it just responds.

If no allowing CORS header client gets no response.

No cookies

@johnwilander

CORS-Unaware ServerClient Server

Ajax POST

Looks likea normal POST.Has a reliable Origin header.

If no authori-zation logic is present it just responds.

If no allowing CORS header client gets no response.

No cookies

@johnwilander

CORS-Unaware ServerClient Server

Looks like a normal request.Has a reliable Origin header.

If no authori-zation logic is present it just responds.

If no allowing CORS header client gets no response.

CookiesAjax + withCredentials = true;

@johnwilander

CORS-Unaware ServerClient Server

OPTIONS request with Access-Control-Request-Method: GET/POST

If no authori-zation logic is present it just responds.

If no allowing CORS header client never sends request.

Preflight requestAjax + setRequestHeader('X-Requested-With','XMLHttpRequest');

@johnwilander

CORS-Aware ServerClient Server

Looks like a normal request.Has a reliable Origin header.

Authorizes the calling origin and includesAccess-Control-Allow-Origin

If allowing CORS header client gets the response.

CookiesAjax + withCredentials = true;

@johnwilander

This means …

• Attackers can now do CSRF without img tags or form posting.

• Servers that don’t check origin headers have no clue it’s a cross-origin Ajax call.

• Custom headers including the Ajax header are effectively dead in the CORS case since developers want to avoid preflights.

@johnwilander

DEMO CORS

@johnwilander

Sandboxed iframes

10+Rumored to

not be there yet

17+

5.1+

22+

Nosupport

4.2+

2.2+

http://caniuse.com/#feat=iframe-sandbox

@johnwilander

main.1-liner.org

<iframe src=”main.1-liner.org” sandbox>

<script></script><form></form>

</iframe>

Sandboxed iframe

@johnwilander

main.1-liner.org

<iframe src=”main.1-liner.org” sandbox=”allow-same- origin”>

<script></script><form></form></iframe>

Sandboxed iframe

@johnwilander

main.1-liner.org

<iframe src=”main.1-liner.org” sandbox=”allow-same- origin allow-scripts”>

<script></script><form></form></iframe>

Sandboxed iframe

@johnwilander

main.1-liner.org

<iframe src=”main.1-liner.org” sandbox=”allow-same- origin allow-scripts allow-forms”><script></script><form></form></iframe>

Sandboxed iframe

@johnwilander

DEMO Sandboxed iframe

@johnwilander

Partialsupport inIE8 – IE10

15+

5.1+

22+

12+

3.2+

2.1+

http://caniuse.com/#search=postMessage

postMessage

@johnwilander

postMessage

• Allows string-based communication between frames and windows

• You need a handle to the target to be able to send a message

• The recipient whitelists origins from which it accepts messages

@johnwilander

postMessage// Sending messages requires a handle to the receiving endvar popup = window.open("http://other.1-liner.org", "_blank");popup.postMessage("Luke, I am your father.", "http://other.1-liner.org");

// Receiving messages requires an event listenerreceiveMessage = function(event) { if (event.origin !== "http://other.1-liner.org") { return; } console.log(event.data);}window.addEventListener("message", receiveMessage, false);

@johnwilander

postMessage// Sending messages requires a handle to the receiving endvar popup = window.open("http://other.1-liner.org", "_blank");popup.postMessage("Luke, I am your father.", "http://other.1-liner.org");

// Receiving messages requires an event listenerreceiveMessage = function(event) { if (event.origin !== "http://other.1-liner.org") { return; } console.log(event.data);}window.addEventListener("message", receiveMessage, false);

Handle to receiving

window or frame

Target origin makes sure the window

or frame hasn’t been redirected

@johnwilander

postMessage// Sending messages requires a handle to the receiving endvar popup = window.open("http://other.1-liner.org", "_blank");popup.postMessage("Luke, I am your father.", "http://other.1-liner.org");

// Receiving messages requires an event listenerreceiveMessage = function(event) { if (event.origin !== "http://other.1-liner.org") { return; } console.log(event.data);}window.addEventListener("message", receiveMessage, false);

Receiving event listener has to check the message

comes from a trusted origin

@johnwilander

DEMO postMessage

@johnwilander

Part 3How To Use the Technologies Securely

@johnwilander

First stop using …

• Jsonp

• document.location trick

• img tag trick

@johnwilander

Implement CORS Server-Side Now

Client ServerAlways check origin header.

Authorize the calling origin based on a whitelist.

If allowing CORS header client gets the response.

CookiesAjax + withCredentials = true;

@johnwilander

HTML

HTML

HTML

CSS

CSS

JavaScript

JavaScript

CSS

JavaScript

Bad Web Architecture

@johnwilander

HTML

CSS on file

JavaScript on file

Import

CSS

JavaScript

Good Web Architecture

@johnwilander

A good separation of content, code, and style

allows for CSP.

@johnwilander

But our legacy applications are a mess.

@johnwilander

HTML

CSS

HTMLCSS

JavaScript

JavaScript

JavaScript

HTML CSS

Placing legacy web and third party webin iframes allowsthe new code to runwith CSP.

iframe

@johnwilander

main.1-liner.org

HTML

CSS

HTMLCSS

JavaScript

JavaScript

JavaScript

HTML CSS

Then set the sandbox directive without allow-same-origin to leverage the same-origin policy for protection fromvulnerabilities in legacy or third party code.

Sandboxed iframe

main.1-liner.org… or …3rdparty.net

@johnwilander

main.1-liner.org

HTML

CSS

HTMLCSS

JavaScript

JavaScript

JavaScript

HTML CSS

A backwards compatible alternative is to move legacy code to anothersubdomain to leverage the same-origin policy for protection fromvulnerabilities in legacy code.

iframe

legacy.1-liner.org

@johnwilander

new.1-liner.org

HTML

CSS

HTMLCSS

JavaScript

JavaScript

JavaScript

HTML CSS

But beware. Legacy code typically doesn’t relocate gracefully. So you might have to keep its domain and get a new subdomain for your new code.

iframe

main.1-liner.org

@johnwilander

main.1-liner.org

HTML

CSS

HTMLCSS

JavaScript

JavaScript

JavaScript

HTML CSS

Use the postMessage API for communicating between your iframes and your main window.

Sandboxed iframe

main.1-liner.org… or …3rdparty.net

postMessage

@johnwilander

main.1-liner.org

CORS from/to 3rdparty.net

HTML

CSS

HTMLCSS

JavaScript

JavaScript

JavaScript

HTML CSS

Sandboxed iframe

main.1-liner.org… or …3rdparty.net

postMessage

Finally, use CORS when retrieving content from third parties and make sure to encode it right before injecting it to the DOM.

@johnwilander

Thanks!