Memoirs of a Developer.

Your Content Security Policy is a security seatbelt

— Trying to implement CSP with Eleventy…

With web sites becoming more and more dynamic, proper security measures are more important than ever. If you're a good developer, then you are probably aware of the many security vulnerabilities that can exist in a web application. You'll probably even make sure your code is as safe as it can be. But how can you be absolutely sure that your application is fully secure?

You can't.

Of course your own code is secure — or is it? And what about those third-party libraries you're using? Or that ad campaign running on the homepage, hosted by an external provider? Your server software? The browsers that your visitors use? Or the network they use to access your web application?

There are so many factors that impact overall security, many of which you have limited or no control over. It's not a question of whether your application can be hacked. The real question is, When my application gets hacked, do I have proper damage control in place?”

That's where a good Content Security Policy comes in.

Fasten your seatbelt

The Content Security Policy (CSP) is a great security feature that was added to Internet browsers several years ago. A CSP basically tells the browser what kinds of content it is allowed to render or execute, and where that content is allowed to originate from.

When the security of your application is compromised, the damage can be greatly limited with a proper CSP in place. Imagine that an attacker has injected a <script> tag into a page. When this script is executed, it exploits other vulnerabilities to take control of the visitor's machine.

You don't want to be known as the person responsible for allowing a hacker to gain access to the computers of your users.

Photo: Jordan Bebek, Unsplash

With a CSP, you can tell the browser to never execute inline scripts. So in the scenario outlined above, the malicious script will never be able to do any damage. You can also control from which whitelisted locations scripts can be loaded. And CSP gives you similar control over other content, such as images and CSS.

It's important though to realize that the application was still hacked. Having a Content Security Policy in place will safeguard you from further damage, but it won't make your application secure.

In other words, your car still crashed, but because you were wearing a seatbelt you only ended up with a few scratches. But wearing a seatbelt won't prevent you from crashing.

A couple of small confessions

This article was sparked by some decisions I made while building this web site. Knowing that this would be a very simple content site, I decided to go with a PHP web host rather than a more expensive ASP.NET alternative. Although I started optimistically with PHP, I soon realized two things:

  1. Even a seemingly simple site like this still needs quite some programming.
  2. PHP still is as much a pain to write as it was many years ago.

So instead, I went with the static site generator Eleventy and it's been a delightful experience so far.

My Content Security Policy had been set up to disallow any kind of inline content. So when I implemented the hero image at the top of the page using an inline <style> tag, the browser simply said, “Nope, ain't gonna happen.”

Fortunately, CSP offers two ways to selectively allow certain inline style declarations while blocking all others: a nonce or a hash source. With the first option you include a nonce (a random value, ideally different on each request) in the CSP header and the <style> tag, tying them together. With the second option you calculate a hash over the contents of the <style> tag and include that hash in the CSP header.

Unfortunately, Eleventy is a static site generator, making both of these dynamic options rather cumbersome.

Not willing to jump through all kinds of hoops to implement dynamic CSP directives in a static site, I took a step back and asked myself what I was trying to solve. What was my CSP supposed to protect against?

Why I don't wear a five-point harness

A Content Security Policy is great when you have a web application with dynamic, user-generated content. But in my case I was building a completely static site. It's only HTML, CSS and a few images. There is no server-side rendering or database content. There's not even any JavaScript. So why should I bother implementing a CSP with dynamic nonces for my <style> tags if I have complete control over my content anyway?

Because the content is completely static, the only way for an attacker to inject any <style> tags in my site is by gaining access to my hosting environment. And if they can do that, they can also change the CSP header to their liking.

You shouldn't implemented a Content Security Policy without considering how it can benefit your application. Application security is a delicate topic that requires thorough understanding. If you implement a CSP just for the sake of having one without understanding the threats it needs to protect against, you're missing the point.

In my case, by using a static site generator the attack surface was greatly reduced. In the current state of the site, having a CSP doesn't add much value. However, future developments may introduce security vulnerabilities. By having a CSP in place I will be alerted when the site needs additional privileges. More importantly, it will act as a safeguard in case I make a mistake and limit the consequences for you, a visitor of this site.

Whenever I get in my car, I don't intend to crash. But I still wear a seatbelt, just to be on the safe side. And so should you.

Further reading

Writing perfect code: a story on paralysis analysis

In my early days as a software developer, I was only concerned with the results of the code I wrote. But as I progressed, I started thinking about the code itself, not only its outcome. After I while I found myself getting stuck, thinking too much about every little detail.

How git rebase can break your history

A git feature that is commonly seen as an advantage is being able to rewrite history. This ability allows for features such as rebase to exist. Git users seem to prefer rebasing over merge commits, because it keeps the commit history clean. But does that justify breaking your history?