HTTP Security Headers are often the last thing a developer wants to think about when creating a new site but are critical to your website’s security. HTTP Security Header Misconfiguration is featured in the current OWASP Top 10 2017 and even before that in the OWASP Top 10 2013 when it was spread through multiple issues. If you are unfamiliar with OSWAP, I recommend reading about them.
I want to prove three things in this post, the first is that this is an issue you should take seriously. The second is that the process of configuring security headers is easier than you think, and lastly that as good as security headers are, they are used to strengthen your site, not shield you completely.
There is no better explanation than to demonstrate with real-world examples. I want to redirect you to a simple site https://securityheaders.com/ which shows your current security header configuration and potential issues with it.
Let’s throw my blog in and see what happens.
https://blog.jeremyshaw.co.nz is a Firebase hosted static site, and I have given no effort whatsoever on security headers just yet so this is the default configuration. As you can see I am missing numerous headers, opening up a variety of ways my site could be at risk.
I don’t have time to go through the severity of each of the missing headers so I will just acknowledge the first in the list, Content-Security-Policy.
A Content-Secruity-Policy (CSP) is just a list of approved content that can be loaded on your site. You whitelist which content can come from which destination. By setting
img-src 'self' for instance, only images that are from my domain will be shown.
As a blog, I have enabled comments through Disqus and constantly reference images from other domains in blog posts. This means I am completely reliant on Disqus’s comment validation and that none of those external references change their content to some malicious script to exploit the readers of my blog. This is known as a stored cross site scripting (XSS) attack.
Let’s say a comment gets through Disqus validation and posts the content below.
This malicious code script will execute on the page load, regardless if you have read the comments or not. By setting the CSP to
script-src 'self' https://www.gstatic.com/firebasejs/ https://cdn.jsdelivr.net/algoliasearch/3/algoliasearch.min.js https://cdn.jsdelivr.net/autocomplete.js/0/autocomplete.min.js https://c.disquscdn.com/ https://io.narrative.io/ https://referrer.disqus.com/"
Here is another example of how this works using an SVG. Credit to http://ghostlulz.com/xss-svg/ for the code.
<img src="/images/20210519/xss.svg" alt="XSS Example SVG">
You will notice that the script is not being executed when you loaded this blog post. Using an
img element to execute XSS is called a direct XSS attack as the user has to go directly to the image source, in this case https://blog.jeremyshaw.co.nz/images/20210519/xss.svg.
If you are brave to view it directly, you will continue to find the script is not executed. We again owe this to the
script-src 'self' CSP.
I implore you to look at the security headers of your sites and see if you are subject to vulnerabilities too.
What your configuration should look like depends on how your site is structured and how restrictive you can be. You can generate your CSP through something like https://report-uri.com/home/generate with the other major headers being more boilerplate as shown below.
How you configure security headers depends entirely on how your site is hosted. Some static site hosts such as Netlify and Firebase make it as easy as editing your hosting config file. The JSON above is an example of how this is set in Firebase. If you are using apache or Nginx, the process is almost just as easy with you editing the .conf file in
If you are in neither of those camps don’t worry, security headers can also be set as meta tags in your HTML head such as below.
<meta http-equiv="Content-Security-Policy" content="default-src 'self'">
This approach is not considered as strong as headers through the server are enforced by the browser whereas a meta tag requires the HTML to be parsed. A meta tag would not prevent the first XSS SVG example for instance.
Security headers should be used to strengthen your defense, but should not be your only safeguard.
Let’s look at CSP again. There is a reason why I choose to use SVG script injection as an example. We have seen that it can help prevent scripts from being activated using script-src self, however, if we take the same SVG code and now use and object element to load it, we will see it bypasses any CSP we have set.
Using an object, embed, or iframe element is an indirect XSS attack as this time the script code can be executed without viewing the image directly. Press the button below if you wish to load the same SVG through an object tag.
The iframe does allow for a
csp attribute, the object and embed don’t. This means that there is effectively no way through CSP to prevent the script from executing without completely blocking the SVG using something like
object-src 'none'. Another fun fact, this is not unique to CSP but rather it is not possible to achieve this with any security header. Therein lies the limitations of security headers. You still need to be smart about the code you are writing, even on the frontend. Please don’t use object or embed tags!
The final result.
Thank you for reading. I hope you have picked up something new about HTTP security headers and their use in modern web development. It is easy to check how secure you are using https://securityheaders.com/ and use https://report-uri.com/home/generate to quickly create your CSP if you haven’t already!