tl;dr in this blog post we are going to give a look at modern browsers's protection with some hands on example available at https://github.com/asanso/browsers-security and deployed in Heroku. This blog post is NOT about Same-origin policy
Introduction
In this blog post we are going to give a look at modern browsers's protection. More specifically if you are designing a REST API where the result response
is driven by some user input, then why not have some help from the
browser rather than brewing some ad hoc protection?
I am going to provide some demo deployed in Heroku .
If you prefer running them on your machine you might want to clone https://github.com/asanso/browsers-security and drill down into the specific example.
Mind your content type
By definition Content-Type entity-header field indicates the media type of the
entity-body sent to the recipient or, in the case of the HEAD method,
the media type that would have been sent had the request been a GET.
It turns out that returning the proper Content-Type might save a lot of headache.
INSTANT DEMO: https://mysterious-ocean-4724.herokuapp.com/?name=%3Cscript%3Ealert%28document.domain%29%3C/script%3E
Or from your browsers-security check out:
- cd contentType
- npm install
- node helloWorldJson.js
- and with the browser hit http://localhost:9002?name=<script>alert(document.domain)</script>
curl -v -L "https://mysterious-ocean-4724.herokuapp.com/?name=<script>alert(document.domain)</script>"
>
< HTTP/1.1 200 OK
* Server Cowboy is not blacklisted
< Server: Cowboy
< Connection: keep-alive
< X-Powered-By: Express
< Content-Type: text/html; charset=utf-8
< Content-Length: 42
< Etag: W/"2a-QK3v/EQbwe/c0QdPJrXydw"
< Date: Wed, 02 Dec 2015 15:16:31 GMT
< Via: 1.1 vegur
<
{"helloWorld": "<script>alert(document.domain)</script>"}
As you can see we are returning some JSON payload in the response but using the "wrong" Content-Type (aka text/html). This in combination with a malicious input provided by an attacker will make the browser to happily execute the provided javascript snippet.
Now of course output sanitization (this is always good BTW) would have stopped this attack but this would have required some effort. From the other hand just returning the right Content-Type (application/json in this example ) will make the browser displaying the JSON text content as in this example
https://mysterious-ocean-4724.herokuapp.com/?surname=%3Cscript%3Ealert%28document.domain%29%3C/script%3E
Bonus Part:
The examples above where targetting a stored XSS. Those are cross browsers and if successful (namley some stored javascript is displayed in some not sanitized output) every browser will happiliy execute the javascript. For reflected XSS (where the input is bounced directly in the output) some browsers (Chrome, Safari, IE ) ship with an XSS filter. E.g. try to hit the follow link with Google Chrome
has the result
and the XSS is then stopped by the browser. From the other hand Firefox would still be vulnerable.
https://mysterious-ocean-4724.herokuapp.com/?surname=%3Cscript%3Ealert%28document.domain%29%3C/script%3E
curl -v -L "https://mysterious-ocean-4724.herokuapp.com/?surname=<script>alert(document.domain)</script>"
< HTTP/1.1 200 OK
* Server Cowboy is not blacklisted
< Server: Cowboy
< Connection: keep-alive
< X-Powered-By: Express
< Content-Type: application/json; charset=utf-8
< Content-Length: 56
< Etag: W/"38-AEX4mYlsmzOHSw8oOicxiQ"
< Date: Mon, 07 Dec 2015 09:39:53 GMT
< Via: 1.1 vegur
<
{"helloWorld":"<script>alert(document.domain)</script>"}
* Server Cowboy is not blacklisted
< Server: Cowboy
< Connection: keep-alive
< X-Powered-By: Express
< Content-Type: application/json; charset=utf-8
< Content-Length: 56
< Etag: W/"38-AEX4mYlsmzOHSw8oOicxiQ"
< Date: Mon, 07 Dec 2015 09:39:53 GMT
< Via: 1.1 vegur
<
{"helloWorld":"<script>alert(document.domain)</script>"}
Bonus Part:
The examples above where targetting a stored XSS. Those are cross browsers and if successful (namley some stored javascript is displayed in some not sanitized output) every browser will happiliy execute the javascript. For reflected XSS (where the input is bounced directly in the output) some browsers (Chrome, Safari, IE ) ship with an XSS filter. E.g. try to hit the follow link with Google Chrome
has the result
The XSS Auditor refused to execute a script in 'https://mysterious-ocean-4724.herokuapp.com/?title=%3Cscript%3Ealert%28document.domain%29%3C/script%3E' because its source code was found within the request.
and the XSS is then stopped by the browser. From the other hand Firefox would still be vulnerable.
Re-mind your content type
As returning a "wrong" content type you might imagine that not returning a Content-Type AT ALL is NOT a so great idea :) Indeed there are some browsers (did I say IE :)?) that trying to be
extra clever and try to intelligently interpret the response content in
order to guess the right Content-Type. In the netsec jargon this is call sniffing. But let's the example talking on its own, using IE ONLY
https://protected-garden-1595.herokuapp.com?name=<script>alert(document.domain)</script>
Again if you prefer running in local then clone https://github.com/asanso/browsers-security/tree/master/noContentType
Trying to inspect the response we can see the total lack of content type:
curl -v -L "https://protected-garden-1595.herokuapp.com?name=<script>alert(document.domain)</script>"
< HTTP/1.1 200 OK
* Server Cowboy is not blacklisted
< Server: Cowboy
< Connection: keep-alive
< Date: Mon, 07 Dec 2015 10:52:54 GMT
< Content-Length: 51
< Via: 1.1 vegur
<
Hello World <script>alert(document.domain)</script>
The solution is obviously is to return the correct Content-Type hence
TIL: mind you Content-Type
Coming soon...
This concludes the part #1. If you like this stuff you might watch this space for:- more about Content-Type
- nosniff
- X-XSS-Protection
- Content-Disposition
- Content Security Policy (CSP)
- Cross-origin resource sharing (CORS)
- HTTP Strict Transport Security (HSTS)
- Subresource Integrity (SRI)
Comments