I have been trying to explain the importance of HTTP headers to some younger/junior devs I am working with. I have noticed that headers are often considered to be 'too technical' or even 'old tech'.
I'm going to recommend them to read this, but I do think I need to explain a couple of things that the article is not clear about:
- Be sure that you understand the concept of HSTS! Simply copy/pasting the example from this article will completely break subdomains that are not HTTPS enabled and preloading will break it permanently. I wish the authors made that more clear. Don't use includesubdomains and preload unless you know what you are doing. Scott Helme also did a great article about this [0].
- CSP can be really hard to set up. For instance: if you include google analytics, you need to set a script-src and a img-src. The article does a good job of explaining you should use CSP monitoring (I recommend sentry), but it doesn't explain how deceptive it can be. You'll get tons of reports of CSP exceptions caused by browsers plugins that attempt to inject CSS or JS. You must learn to distinguish which errors you can fix, and which are out of your control.
- Modern popular frontend frameworks will be broken by CSP as they rely heavily on injecting CSS (a concept known as JSS or 'styled components'). As these techniques are often adopted by less experienced devs, you'll see many 'solutions' on StackOverflow and Github that you should set unsafe-inline in your CSP. This is bad advise as it will bascially disable CSP! I have attempted to raise awareness in the past but I always got the 'you're holding it wrong' reply (even on HN). The real solution is that your build system should separate the CSS from JS during build time. Not many popular build systems (such as create-react-app) support this.
- Cache control can be really hard too. If you don't have time to fiddle with these settings, I recommend using a host like Netlify, they seem to do a proper job at caching in my experience.
> - Be sure that you understand the concept of HSTS! Simply copy/pasting the example from this article will completely break subdomains that are not HTTPS enabled and preloading will break it permanently. I wish the authors made that more clear. Don't use includesubdomains and preload unless you know what you are doing. Scott Helme also did a great article about this.
Great point that is even more important than people might realize at first when reading your comment, because it doesn't cover only your web properties; it covers all subdomains even those you forget you use and don't associate with your web presence.
You have a nas at nas.exemple.com, a webmail at mail.exemple.com, a third party cdn/similar routing at cdn.exemple.com, a printer interface at printer.office.exemple.com and other internals things at internally routed path, like that custom old dashboard written 15 years ago ? Include subdomain covers it, and preloading means you can't disable it once you realize you shouldn't have done it.
Seen a few small non-IT companies hit by that, and then asking for urgent consultancy to fix it.
> and then asking for urgent consultancy to fix it.
If it is preloaded (as the article suggest you should do), there is nothing you can do to 'fix' it, except migrating all other subdomains to HTTPS. Which is not always possible.
Oh yes exactly, especially since they don't have much planned budget for it (small firms) it ends either with a lot of dirty proxying to have an HTTPS middle point, just because someone screwed up without paying attention to what they were actually doing, or with a secondary domain for the office.
My subdomains for things like that all go through a reverse-proxy (Caddy) where I terminate HTTPS, which makes things simple, though I still don't use includesubdomains because it just seems too risky.
One tip from personal experience: I usually introduce CORS (and Access-Control-Allow-Origin header in particular) first. Most people unfamiliar with HTTP headers have no problem understanding the usefulness of that one and it opens the door to other stuff.
I'm surprised that SRI (sub resource integrity) isn't mentioned, though it does require HTML changes. It makes you embed SHA-2 hashes into your CSS and/or JS tags, and if the given hash doesn't match the received resource's hash, the resource isn't applied to the page. It protects you (for example) from your CDN changing your data.
Also, no mention of E-tags. They help with caching.
> Be sure that you understand the concept of HSTS! Simply copy/pasting the example from this article will completely break subdomains that are not HTTPS enabled and preloading will break it permanently
+1. I'm kind of worried that the default copy-pasteable snippet there can lead to serious consequences. Well, less today when you can get certs for free, but you may have some unpleasent downtime.
> The real solution is that your build system should separate the CSS from JS during build time. Not many popular build systems (such as create-react-app) support this.
The real solution is to fix the actual protocol issues, instead of imposing arbitrary limitations on what scripts can do with the page. If you pause and think about what these security measures are aiming to mitigate, they themselves are at best amateurish.
It could be that headers seem scary, and I think that is because you can get a lot done without them, so they might be unfamiliar to junior devs. To make them less scary maybe it is sufficient to say they are just key/value pairs, like an additional JSON object in the response. It's there for you to see in Chrome dev tools, and it's fun seeing how different sites might send different headers.
As a motivator I always say look at the network tab and think about how you can make stuff faster. It was this mindset where I first saw these additional requests and thought 'WTF!' I better find out why it is happening, turned out to be preflight requests.
> I think that is because you can get a lot done without them
Reinventing the wheel is also a good way to fool yourself into assuming we're being clever and productive. I mean, it's as if everyone who preceded me in a field was incapable of looking into a problem I've managed to find a hacky solution in 5 minutes.
Meanwhile let's not pretend that there are currently about half dozen standards and specifications for HATEOAS, all involving ugly hacks around response documents and funny media types, when all it takes to achieve the same goal is passing link relations through Link headers.
Never been asked about headers in a job interview, junior or otherwise. Usually interviews are a 'can you code' test. I've never been interviewed by FAANG etc. though.
I was also disappointed that some aspects of modern JS frameworks make adding strict CSPs incredibly hard. Especially with CSS there don't seem to be many resources on how to make something like React styled components work with CSPs, disabling the CSS CSP seems to be the only reasonable way to do this.
> Modern popular frontend frameworks will be broken by CSP as they rely heavily on injecting CSS (a concept known as JSS or 'styled components')...
Not sure what you mean by this paragraph. JSS and styled components are both specific JS libraries that provide a way to write styles in JS and compile them to normal CSS. They both support normal server-side rendering of CSS that could be included in the HTML like any traditional web page.
The general term for this concept is “CSS-in-JS.” The libraries do not rely on inline styles, even though the general term may suggest that.
I’m not very familiar with JSS, but styled components absolutely supports building CSS in create-react-app.
I confused styled components with JSS. JSS is a library used by popular UX libraries, most notably Material-UI.
The problem is that JSS injects the CSS dynamically at runtime. There seems to be no real solution to extract the CSS into a .css file at compile time.
The only solution that the JSS developers suggest [0] is using server-side rendering to inject a nonce into the HTML script tag to bypass CSP. But I think that requiring server-side rendering for a client-side rendered framework is totally backwards. The whole reason why I use single page client side rendered applications is so I can deploy on a CDN.
Another gotcha with HSTS + includesubdomains is if you have a naked domain e.g. https://example.com redirecting to a www prefix e.g. https://www.example.com, but the server is configured to send the HSTS header for the naked domain.
It's not always obvious because your gut reaction is "oh my web site is on www.", but that misconfigured naked domain redirect might indeed break "randomservice.example.com".
> I have noticed that headers are often considered to be 'too technical' or even 'old tech'.
Are you or they able to elaborate on that? Headers are an active area of research/implementation for web security, are they working on any replacement to the entire concept?
> Be sure that you understand the concept of HSTS!
Instead of using HSTS, you can also simply redirect any HTTP request to HTTPS. That way, you are certain that HTTPS is used, even if a browser does not understand HSTS.
The limitation with the approach (of HTTP=>HTTPS redirects) is that your average coffee-shop-wifi-user may not notice if their connection does not upgrade to HTTPS due to malicious interception of their connections.
With HSTS, once they've connected to the server over HTTPS once (e.g. at home), every connection from that browser will be immediately upgraded to HTTPS before even trying HTTP.
Your suggestion is valid - as HSTS is only delivered over HTTPS - and the upgrade is still required the first time.
This will leave your users vulnerable to man-in-the-middle attacks. If I control the router between their computer and the Internet, I can serve back a HTTP page which doesn't redirect, and trick them to enter their password (for example).
It can't, that is what preloading is for. Your browsers comes preloaded with a list of all sites that have requested HSTS preload, so your browser will use HTTPS even on the first visit. This is why preloading on all subdomains is potentially dangerous to use, it could break your site if you don't have HTTPS everywhere.
But even without preloading HSTS will improve security. Yes, the first visit will be susceptible to MITM, but every visit after that is not. This makes it a lot more difficult for an attacker as they must intercept the very first visit for the attack to work.
And because the preload list is hierarchical whole swathes of the Web can be covered with a single entry. .dev is the biggest example, but they can protect all the stack exchanges, all the default blogspot blogs, that sort of thing.
It can't! But after the first time it's been contacted, when you contact it again HSTS will enforce HTTPS (from the client itself - much stronger than a redirect).
> Instead of using HSTS, you can also simply redirect any HTTP request to HTTPS
I think this comment sums up my whole point about how less experienced developers must learn how to use headers.
Like others have commented, HSTS is used to fix potential dangers in forwarding (MITM attacks), it also reduces overhead.
> That way, you are certain that HTTPS is used, even if a browser does not understand HSTS.
If you use HSTS, you should always use a 301 permanent redirect as a fallback method for old browsers and other HTTP clients (like some libCurl implementations).
I think what this comment thread really says is that the burden of fixing mistakes in the web's fundamental design is put on the individual developer and that this is another mistake!
If MITM is a serious issue then it's an extremely bad idea to depend on individual developers of every website out there to mitigate this.
Everything else is just wishful thinking, though. Chrome cannot decide to just deprecate (e.g.) unsafe-inline CSS tags. It would break 90% of websites, and people would move to a different browser to get their websites to work.
Yeah but than you always generate an additional request, with HSTS cached, browser with fetch the https version without an additonal redirect. I wrote a post about it some time ago https://pawelurbanek.com/amp-seo-rating-performance
I'm going to recommend them to read this, but I do think I need to explain a couple of things that the article is not clear about:
- Be sure that you understand the concept of HSTS! Simply copy/pasting the example from this article will completely break subdomains that are not HTTPS enabled and preloading will break it permanently. I wish the authors made that more clear. Don't use includesubdomains and preload unless you know what you are doing. Scott Helme also did a great article about this [0].
- CSP can be really hard to set up. For instance: if you include google analytics, you need to set a script-src and a img-src. The article does a good job of explaining you should use CSP monitoring (I recommend sentry), but it doesn't explain how deceptive it can be. You'll get tons of reports of CSP exceptions caused by browsers plugins that attempt to inject CSS or JS. You must learn to distinguish which errors you can fix, and which are out of your control.
- Modern popular frontend frameworks will be broken by CSP as they rely heavily on injecting CSS (a concept known as JSS or 'styled components'). As these techniques are often adopted by less experienced devs, you'll see many 'solutions' on StackOverflow and Github that you should set unsafe-inline in your CSP. This is bad advise as it will bascially disable CSP! I have attempted to raise awareness in the past but I always got the 'you're holding it wrong' reply (even on HN). The real solution is that your build system should separate the CSS from JS during build time. Not many popular build systems (such as create-react-app) support this.
- Cache control can be really hard too. If you don't have time to fiddle with these settings, I recommend using a host like Netlify, they seem to do a proper job at caching in my experience.
[0] https://scotthelme.co.uk/tag/hsts-preload/
edit: typos