Introducing you to browser security headers on Pluralsight

I’ve been doing this fantastic demo about browser security headers in a lot of my recent talks and workshops. It’s always a lot of fun and it’s very interactive – you can try this out for yourself right now – and it works like this:

So cross site scripting (XSS) is still a big thing. Yes it’s been around for ages and yes we should be on top of it by now, but here we are. Anyway, I was at the AppSecEU conference in the Netherlands a few months ago and a local guy called Breno de Winter did a fantastic talk in which he illustrated the prevalence of XSS by showing a loud, obnoxious and impactful video of sites demonstrating the vulnerability. It looks just like this:

This is pretty much exactly what it looks like – Dutch banks doing the Harlem Shake. Awesome. Except it isn’t really because you don’t want your bank doing the Harlem Shake! When someone can modify the behaviour on the page by reflecting arbitrary content from the URL into the HTML source (and that’s exactly what we’re seeing here – reflected XSS), they may reflect all sorts of other things into there too. A script that steals authentication cookies and enables session hijacking is a perfect example.

Now I know what you’re thinking – you’d like to get sites to do the Harlem Shake too, right? Course ya do! Who wouldn’t, all that dancing around and nutty music is kinda cool and the good news is that it’s easy for you to get any site to do the Harlem Shake (well almost any, this is sort of the point). Even better news is that you don’t need to find an XSS risk and worry about someone throwing the book at you for pwning their site.

Here’s how it works: firstly, copy the following script into your clipboard:

javascript:(function(){function c(){var e=document.createElement("link");e.setAttribute("type","text/css");e.setAttribute("rel","stylesheet");e.setAttribute("href",f);e.setAttribute("class",l);document.body.appendChild(e)}function h(){var e=document.getElementsByClassName(l);for(var t=0;t<e.length;t++){document.body.removeChild(e[t])}}function p(){var e=document.createElement("div");e.setAttribute("class",a);document.body.appendChild(e);setTimeout(function(){document.body.removeChild(e)},100)}function d(e){return{height:e.offsetHeight,width:e.offsetWidth}}function v(i){var s=d(i);return s.height>e&&s.height<n&&s.width>t&&s.width<r}function m(e){var t=e;var n=0;while(!!t){n+=t.offsetTop;t=t.offsetParent}return n}function g(){var e=document.documentElement;if(!!window.innerWidth){return window.innerHeight}else if(e&&!isNaN(e.clientHeight)){return e.clientHeight}return 0}function y(){if(window.pageYOffset){return window.pageYOffset}return Math.max(document.documentElement.scrollTop,document.body.scrollTop)}function E(e){var t=m(e);return t>=w&&t<=b+w}function S(){var e=document.createElement("audio");e.setAttribute("class",l);e.src=i;e.loop=false;e.addEventListener("canplay",function(){setTimeout(function(){x(k)},500);setTimeout(function(){N();p();for(var e=0;e<O.length;e++){T(O[e])}},15500)},true);e.addEventListener("ended",function(){N();h()},true);e.innerHTML=" <p>If you are reading this, it is because your browser does not support the audio element. We recommend that you get a new browser.</p> <p>";document.body.appendChild(e);}function x(e){e.className+=" "+s+" "+o}function T(e){e.className+=" "+s+" "+u[Math.floor(Math.random()*u.length)]}function N(){var e=document.getElementsByClassName(s);var t=new RegExp("\\b"+s+"\\b");for(var n=0;n<e.length;){e[n].className=e[n].className.replace(t,"")}}var e=30;var t=30;var n=350;var r=350;var i="//";var s="mw-harlem_shake_me";var o="im_first";var u=["im_drunk","im_baked","im_trippin","im_blown"];var a="mw-strobe_light";var f="//";var l="mw_added_css";var b=g();var w=y();var C=document.getElementsByTagName("*");var k=null;for(var L=0;L<C.length;L++){var A=C[L];if(v(A)){if(E(A)){k=A;break}}}if(A===null){console.warn("Could not find a node of the right size. Please try a different page.");return}c();S();var O=[];for(var L=0;L<C.length;L++){var A=C[L];if(v(A)){O.push(A)}}})()

Now go to your favourite browser and load up a site. If you’re looking for inspiration, works well as does and just for a bit of variety, so does Go to any one of these sites, hit F12 to get the browser’s developer tools up then paste that big whack of script into the console. It should look something like this:

The Harlem Shake script in the browser console

Now you hit “enter” and sit there giggling to yourself like a five year old. Repeat as required until you’ve satisfied yourself that yes, you can cause most websites to do the Harlem Shake by modifying the DOM after the site has been loaded into your browser.

But is this a security risk? I mean the site has already loaded, right? This is your browser doing the dancing, you’ve not exploited some nasty zero day on the website itself, so isn’t this all ok?

To answer this question, try and repeat the process on How’d that work out for you? Yeah, not so good, let’s take a look at the console and find out why no dancing:

CSP errors when attempting to get HIBP to run the Harlem Shake script

We can see two errors in the console, one refusing to load a stylesheet and and another refusing to load media. If you look back at the script earlier on, it does indeed attempt to load both a CSS file and an MP3 and as you’ll see in the error messages, these have been blocked because they violated content security policies. This brings me to the crux of the issue and indeed a large part of what the Pluralsight course is about – CSP. You can easily see the CSP for because it’s returned in the response header of any request to the site, it looks like this:

Content-Security-Policy: default-src 'self';script-src 'self' 'unsafe-inline' 'unsafe-eval';object-src 'none';style-src 'self' 'unsafe-inline';img-src 'self';media-src 'none';font-src 'self';child-src;frame-ancestors 'none';report-uri /WebResource.axd?cspReport=true

A CSP provides a means of the website telling the browser what content it may load and what coding practices it can support. For example, my CSP tells the browser that it can load style sheets from “self” (the same website) and from CloudFlare’s CDN because I embed Bootstrap from there. It also says I can use “unsafe-inline” styles by way of whacking a “style” attribute on an HTML element which I shouldn’t do and I need to get around to fixing. If I didn’t have these entries in the “style-src” directive then the browser would block CloudFlare’s CSS and my inline styles and that’s exactly what it’s doing to the Harlem Shake artefacts; I haven’t allowed them so they don’t get to play on the site.

Getting back to XSS, here’s why this is such a great demo and why CSPs are so important: cross site scripting attacks are successful because they allow the attacker to modify page contents outside the scope of the intended function. This might be via reflected XSS which I touched on earlier, persistent XSS where the attack payload is stored in the database (i.e. via a commenting engine on a website) or DOM based XSS where the DOM on the client is modified. The CSP blocks external content from being loaded so that rules out a whole bunch of attacks that rely on embedding arbitrary resources. The CSP can also block script being rendered into the page and block the use of the JavaScript eval function as well (this is often used to execute a concatenated string of commands). Having a solid CSP means that even if you screw up your XSS defences, the browser simply won’t allow an attack to execute. There are other things you get from CSP as well, such as the ability to stop your site appearing within a frame on someone else’s site (a favourite for clickjacking attacks), and in fact there’s really quite a lot to them.

But CSP is only one of the things I cover in the course, here’s what’s in there:

  1. Understanding Browser Security Headers – how they work and what role they play in defending your app against attackers
  2. HTTP Strict Transport Security (HSTS) – how you can force a site to only ever load over an HTTPS connection (plus I talk about TOFU)
  3. HTTP Public Key Pinning (HPKP) – how you can whitelist the allowable certs a site can load such that the browser won’t trust a fraudulently issued one
  4. Content Security Policy (CSP) – stop your site from doing the Harlem Shake (and actually defend against some serious security risks too)
  5. Tools for Working with Browser Headers – because everyone wants shortcuts, so here they are!

On that last one, a big shout out goes to Scott Helme. Scott has been doing some awesome things with security headers through tools such as and which are great resources for helping you get your headers in check.

Introduction to Browser Security Headers is now live on Pluralsight! I would have written about this when it launched a couple of weeks ago but I got absolutely inundated by the Ashley Madison hack. However that’s given me a bit of a chance to see how the course has performed before writing about it and I’m somewhat astonished that as of now, it’s the 48th most popular course in a library of well over 4,000! I’d wondered if the topic might be a bit niche, but it seems to be really resonating which I’m massively happy about!

Go and check out the course and get yourself some security headers. They’re very cool and they just might save you from a nasty security incident in the future.

Security Pluralsight CSP
Tweet Post Update Email RSS

Hi, I'm Troy Hunt, I write this blog, create courses for Pluralsight and am a Microsoft Regional Director and MVP who travels the world speaking at events and training technology professionals