JS Applications need to exchange data with Backend APIs running on domains other than your own – understanding the same origin policy CSP, CORS and postMessage.
Talk held on Grill.js conference in Wroclaw, Poland on 2018-08-18.
8. JS Applications
need to exchange data with
Backend APIs
running on domains other than your own
SECURELY
9. Challange
Only load code from trusted origins,
don't execute malicious JS code,
don't let people steal your data.
10. Same Origin Policy
For security reasons, browsers restrict cross-
origin HTTP requests initiated from within
scripts.
For example, XMLHttpRequest and the Fetch
API follow the same-origin policy. This means
that a web application using those APIs can
only request HTTP resources from the same
origin the application was loaded from.
– https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS
“
14. Same Origin Policy
A web application using those APIs can only
request HTTP resources from the same origin
the application was loaded from, unless the
response from the other origin includes
the right CORS headers.
– https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS
“
16. Simple GET request via CORS
var req = new XMLHttpRequest();
var url = 'http://bar.other/resources/public-data/';
function callOtherDomain() {
if(req) {
req.open('GET', url, true);
req.onreadystatechange = handler;
req.send();
}
}
17. Simple GET request via CORS
var req = new XMLHttpRequest();
var url = 'http://bar.other/resources/public-data/';
function callOtherDomain() {
if(req) {
req.open('GET', url, true);
req.onreadystatechange = handler;
req.send();
}
}
GET /resources/public-data/ HTTP/1.1
Origin: http://foo.example
HTTP/1.1 200 OK
Access-Control-Allow-Origin: *
Client (foo.example) Server (bar.other)
19. Preflighted POST request via CORS
var invocation = new XMLHttpRequest();
var url = 'http://bar.other/resources/post-here/';
var body = '<?xml version="1.0"?><person><name>Thomas Witt</name></person>';
function callOtherDomain(){
if(invocation)
{
invocation.open('POST', url, true);
invocation.setRequestHeader('Content-Type', 'application/xml');
invocation.onreadystatechange = handler;
invocation.send(body);
}
}
20. Preflighted POST request via CORS - Overview
Client (foo.example) Server (bar.other)
OPTIONS /resources/post-here/ HTTP/1.1
Origin: http://foo.example
Access-Control-Request-Method: POST
Access-Control-Request-Headers: Content-Type
HTTP/1.1 200 OK
Access-Control-Allow-Origin: http://foo.example
Access-Control-Allow-Methods: POST, GET
Access-Control-Allow-Headers: Content-Type
Access-Control-Max-Age: 86400
var invocation = new XMLHttpRequest();
var url = 'http://bar.other/resources/post-here/';
var body = '<?xml version="1.0"?><person><name>Thomas Witt</name></person>';
function callOtherDomain(){
if(invocation)
{
invocation.open('POST', url, true);
invocation.setRequestHeader('Content-Type', 'application/xml');
invocation.onreadystatechange = handler;
invocation.send(body);
}
}
POST /resources/post-here/ HTTP/1.1
Content-Type: text/xml; charset=UTF-8
Origin: http://foo.example
HTTP/1.1 200 OK
Access-Control-Allow-Origin: http://foo.example
PreflightPhaseMainRequestPhase
21. For every new resource accessed,
a preflight request will be issued.
24. CORS vs CSP
CORS
CORS allows a server (bar.other) to give
permission to a site (foo.example) to read
(potentially private) data from a server
(bar.other), using the visitor's browser and
credentials.
CSP
CSP allows a site (foo.example) to prevent itself
from loading (potentially malicious) content
from unexpected sources (e.g. against XSS).
GET /resources/public-data/ HTTP/1.1
Origin: http://foo.example
HTTP/1.1 200 OK
Access-Control-Allow-Origin: *
Content-Security-Policy: base-uri 'none'; default-
src 'self' 'unsafe-inline' data: https: wss:; object-
src 'none'; script-src 'self' https://beta-
api.scrivito.com; upgrade-insecure-requests;
25. 0,133%
of all 1M Alexa sites implement a CSP
June 2015, "CSP adoption: current status and future prospects", Ming Ying / Shu Qin Li
45. window.postMessage vs CORS/CSP
Advantages
CORS is not very simple, postMessage is
No pre-flight requests
No need to modify HTTP headers
Not convinced?
Google uses it as well in it's Google API JS SDK
<iframe name="receiver"
src="https://bar.other/my.js">
<script>
let win = window.frames.receiver;
win.postMessage("message",
"https://bar.other");
</script>
window.addEventListener("message",
function(event) {
if (event.origin !=
'https://foo.example') {
// Unknown domain? Ignore!
return;
}
alert( "Rcvd: " + event.data );
});
46. window.postMessage Security
Security caveats
No side can figure out whether malicious code
has been inserted via XSS!
Check-List
Does the browser support postMessage?
Is the message origin correct and known?
Is the message data sanitized and validated?
Are origins matched using strict equality
(no indexOf(".foo.com") > 0)?
Are messages sent without using wildcards in the
origin?
<iframe name="receiver"
src="https://bar.other/my.js">
<script>
let win = window.frames.receiver;
win.postMessage("message",
"https://bar.other");
</script>
window.addEventListener("message",
function(event) {
if (event.origin !=
'https://foo.example') {
// Unknown domain? Ignore!
return;
}
alert( "Rcvd: " + event.data );
});
48. Xdomain
A pure JavaScript CORS alternative.
No server configuration required - just add a
proxy.html on the domain you wish to
communicate with.
– https://github.com/jpillora/xdomain
“
49. window.postMessage - further reading
developer.mozilla.org/docs/Web/API/Window/postMessage
seclab.stanford.edu/websec/frames/post-message.pdf
labs.detectify.com/2016/12/08/the-pitfalls-of-postmessage/
caniuse.com/#feat=x-doc-messaging