This is the fourth in a series of several posts on how to do way more than you really need to with Let's Encrypt,
certbot, and a good server. I use all of these things regularly but I've never taken the time to take them apart, look at how they work, and spend hours in Google trying in vain to figure out how to put them back together. It was inspired by a disturbing trend of ISP privacy violations and the shocking regulatory capture of the US Federal Communications Commission.
This post looks at a collection of useful security headers. I've tried to explain what each one does, where it can be helpful, and where it might bite you. None of these are absolutely necessary; if nothing else I strongly recommend using HSTS.
- The Series so Far
- Primary Security Reference
- Primary Header Config
- Force Secure Communication
- The Kitchen Sink
- Prevent Clickjacking (Historical)
- Cross-Site Scripting
- Content Sniffing
- Primary Header Config Redux
- But What About...
- Before You Go
- Legal Stuff
The Series so Far
- First Steps
- Tuning with OpenSSL
- Useful Headers
- Generating and Testing a Cert
- Automating Renewals
Things that are still planned but probably not soon:
- Updating OpenSSL
- CSP Playground
- Vagrant Examples (don't hold your breath)
I'm testing out some new tooling. This will be
wotw-highlighter's shakedown run. Let me know what you think about the syntax highlighting! I'm pretty excited because (no whammies) it should work well in AMP and normally.
I wrote the majority of the Apache examples with
httpd in mind, i.e. from a RHEL perspective. If you instead use
apache2, most of the stuff should still work, albeit maybe just a little bit different.
Primary Security Reference
Originally, this post was sourced from a collection of personal experience and interesting sources found during writing. However, once I split this post out, I wanted to find some best practices (my code, while certainly practice, isn't necessarily the best). The Open Web Application Security Project maintains a list of useful headers, which should all be covered here.
EVERYTHING HERE CAN BE SIDESTEPPED. Headers are sent with a request/response, which means they can be completely ignored. Headers do not prevent bad actors from doing malicious things. They do, however, force average users to do things as expected, which usually prevents bad actors from tricking average users into doing malicious things. This is a very important distinction.
Primary Header Config
I like to split the crypto config and header config. I'm always going to want to use a good algorithm, but I might not always want to use, say,
As I said before, I like
Force Secure Communication
As previously mentioned, HSTS ensures users use secure protocols. The HSTS header,
Strict-Transport-Security, has three primary options:
max-age: This specifies the maximum amount of time a user agent (browser) should cache the header. To make things easier, we'll give the cache a half-life of two years:
includeSubdomains: Without including subdomains, there are apparently some cookie attacks that can still be run. However, if you explicitly cannot serve subdomain content securely, this will cause problems. Err on the side of caution but check you subdomains.
preload: You can submit your HSTS site to an external list. This is a long-term commitment, so don't submit your site unless you're sure about config. I won't be using it here because of the extra steps, but I highly recommend it if you've got a stable setup.
HSTS will forcefully break your site if you don't have a proper TLS setup. Remember, it's cached by the user agent, not something you have control over. You can nuke it when necessary, but it is a hassle to do so.
; preload if you're on the list;
; preload if you're on the list;
The Kitchen Sink
Content-Security-Policy header can handle a majority of the other topics here. In theory, CSP defines a secure execution contract. In the past, that was certainly true; the recent spec addition of blackbox code makes it much less secure (e.g. a media policy covers media, not blackbox code that must be run prior to actually running media). That's a personal soapbox, though.
Good CSPs are fairly rigid and explicitly define as much as possible. As such, you might not be able to share them across sites like some of the other headers (i.e. maybe define this per site instead of in
/etc/<server>/common/ssl-headers.conf). For example, a website that serves all its own assets will have a different CSP than a website that uses assets from a CDN.
WARNING: CSPs might break everything if you don't know what you're doing (and if you do know what you're doing, change "might" to "most certainly will"). Luckily you can test things via
Content-Security-Policy-Report until you're confident with the policy. CSPs are awesome but require much more work than the deprecated headers they, in part, replace.
CSPs provide granular source definitions. The
default-src directive is used for anything not specified, so it's a great place to start secure:
Sources themselves have lots of options.
*allows anything, i.e. don't use this
'self'allows content from the same origin
example.comallows content from
https:allows anything over TLS
'unsafe-(inline|eval)'allows inline and dynamic execution and styling
CSP currently defines the following
- AJAX, sockets, and events:
- HTML5 media:
iframes and web workers:
- form actions:
For example, suppose you're serving all your own content but need a Google font to maintain consistent styling.
1 2 3 4 5 6 7
If you're loading lots of external content, an explicit CSP might not be practical. It's always a good idea to specify as much as possible, though. For example, this allows anything over TLS with some caveats on not markup:
1 2 3 4 5 6 7 8
CSPs provide two directives that are useful for frames:
frame-ancestors. The first adds extra security when serving explicitly embedded content; the second adds extra security to all content.
I actually couldn't find any good examples of a CSP
sandbox policy. All of the sources I found looked like the MDN CSP
sandbox page, with a note about how the CSP
sandbox mimics the
sandbox attribute and a list of possible values. Without usage examples, my assumption is that it exists to provide an extra layer of
sandbox security. Anyone can strip the
sandbox tag from an
iframe or change its values; by setting it in the header itself you can limit the options available to external consumers.
I Made This
I was trying to figure out how everything worked together, so I built a small tool to play with everything together. It's really interesting stuff, especially if you do the ad thing. I also split off everything you need to ruin CSP for a quick reference. You should just be able to clone and go.
Prevent Clickjacking (Historical)
Note that this is superceded by a solid
iframes make everything difficult. One of the simplest possible attacks is to drop your content into an
iframe and snoop the interaction. It's not always malicious; some people always try to embed things (still, in 2017) so they can do their own thing. The
X-Frame-Options header gives you
iframe control in user agents that support it.
The majority of websites don't want to be embedded and should probably use
deny, which prevents user agents that respect the header from embedding it. Some sites embed their own content but do not want others to embed it, which is captured by
sameorigin. Finally, you can allow a single external site to embed your content via
XSS is a pretty neat little industry. I know a couple of guys that are still collecting income on exploits they found years ago. Creating exploits requires a lot of ingenuity and even more time.
Which means you should go out of your way to prevent it. No matter how clever you think you are, there's always someone smarter. More importantly, there's always a fresh cadre of new script kiddies that do things you've never thought of. As your codebase ages, low-hanging fruit like XSS headers are more useful than you might think.
Multipurpose Internet Mail Extension types are really easy to pass around. They're fast to use and there are so many of them. However, they're equally easy to take advantage of.
- You should try to protect the privacy of your users as much as possible. You don't need to know where they came from and you don't need to tell anyone else when they leave.
- Most of the internet works off the Referer header now. I've tried at various stages to get away from it without any luck. I might not want everything I do bought and sold by data firms and ad shops, but they don't really care and it's not going away any time soon.
You can beef up the Referer if you'd like, but you should do some serious testing on your apps first to make sure you won't be shooting yourself in the foot.
Primary Header Config Redux
I've left out things that could be problematic everywhere. You might need to consider tweaking
X-Frame-Options if your content gets embedded.
1 2 3 4
nth time, I'd like to reiterate that I haven't actually tested this config. I will. Eventually.
1 2 3 4
But What About...
Public Key Pinning
I didn't include HTTP Public Key Pinning because it's pretty easy to screw up. As Let's Encrypt certs aren't necessarily as stable as commercial alternatives (i.e. may change more frequently without manual intervention), I want to do more research on this.
I spent thirty minutes trying to come up with a good reason for a
crossdomain.xml policy. If you're not doing anything big with Flash or PDFs, I just don't see why you'd bother, especially with a good CSP. Personally, I'd recommend either
master-only if you need a policy at all.
Before You Go
Let's Encrypt is a fantastic service. If you like what they do, i.e. appreciate how accessible they've made secure web traffic, please donate. EFF's
certbot is what powers my site (and basically anything I work on these days); consider buying them a beer (it's really just a donate link but you catch my drift).
I'm still pretty new to the whole CYA legal thing. I really like everything I've covered here, and I've done my best to respect individual legal policies. If I screwed something up, please send me an email ASAP so I can fix it.
- The Electronic Frontier Foundation and
certbotare covered by EFF's generous copyright. As far as I know, it's all under CC BY 3.0 US. I made a few minor tweaks to build the banner image but tried to respect the trademark. I don't know who the
certbotlogo artist is but I really wish I did because it's a fantastic piece of art.
- Let's Encrypt is trademarked. Its logo uses CC BY-NC 4.0. I made a few minor tweaks to build the banner image but tried to respect the trademark.
- I didn't find anything definitive (other than EULAs) covering Nginx, which doesn't mean it doesn't exist. Assets were taken from its press page.
- Apache content was sourced from its press page. It provides a full trademark policy.