Mastodon server security basics: How to get an A+ in the xyz list

The instance list assigns a security rating to your instance based in part on your instance’s score on the Mozilla Observatory, which itself is a composite of a bunch of different site security tests. (The score also uses another set of tests located on tls.imirhil.fr, but that site appears to be down right now.)

This means that in order to climb in the mastodon server ranks, you need to implement Mozilla’s recommendations. This is a good thing—everything the Observatory checks for is good, and if you’re striving for a high score, you’re doing yourself and the Internet a favor by becoming more secure.

At least for now, the max score you can get on the Mozilla Observatory is 135:

<img src="//discourse-cloud-file-uploads.s3.dualstack.us-west-2.amazonaws.com/standard11/uploads/joinmastodon/original/1X/c82a2d2991c035bd18dd93482e9581c848a2126c.png" width="630"height=“500”>

Hitting that score requires properly implementing a whole mess of different security measures. Some of them, like X-XSS protection, are extremely easy—just a quick header to add and you’re done. Others like HSTS and HPKP can actually knock your site offline if implemented incorrectly.

Easy fixes

  • Cookies - The highest score possible is 0. You don’t have to do anything with this setting, as Mastodon handles its cookies satisfactorily by default.

  • Cross-origin Resource Sharing - The highest score possible is 0. You shouldn’t have to do anything with this line item by default—it refers to your site’s Access-Control-Allow-Origin header and Mastodon sets it correctly by default.

  • Referrer Policy - The highest score possible is 5. This line item refers to the Referrer-Policy HTTP header, which controls among other things whether or not your site’s images can be hotlinked (it controls a lot more than that—the header is used by remote browsers to understand when they are and aren’t allowed to load any content from your site).

    Set this header to either no-referrer, same-origin, strict-origin or strict-origin-when-cross-origin for the +5 score. Be mindful that there are security implications to these choices and you should pick the option that fits your needs.

  • Subresource Integrity - The highest score possible is 5. This is another item you don’t have to do anything with, as SRI is implemented natively in Mastodon as of version 1.2.

  • X-Content-Type-Options - The highest score possible is 0. This header tells the browser not to MIME sniff and instead to use the site’s declared content types. This header can only be set one way: X-Content-Type-Options: "nosniff".

  • X-XSS-Protection - The highest score possible is 0. This header tells visiting browsers how to use their built-in cross-site scripting filters. Per security researcher Scott Helme, you want this set to X-XSS-Protection = "1; mode=block".

Medium-ish fixes

  • Content Security Policy - The highest possible score is 10. CSP tells visitors’ browsers what kinds of content can be loaded, and under what context. This is an extremely flexible setting and you can control your site’s resources with a high level of granularity; it’s also a pain in the butt to get right.

    The Content-Security-Policy string that appears to work best with Mastodon (for now) is as follows. Obviously, replace the example URL with your own.

   "frame-ancestors 'none'; default-src 'none'; script-src 'self'; object-src 'self'; style-src 'self' https://fonts.googleapis.com/ 'sha256-deDIoPlRijnpfbTDYsK+8JmDfUBmpwpnb0L/SUV8NeU=' 'sha256-dsU/1vqKDGkqSAJVJCzaxo3alnWrTU/iQqFREt5QB+g='; img-src * data:; media-src 'self' data:; frame-src 'none'; font-src 'self' data: https://fonts.gstatic.com/; connect-src 'self' wss://mastodon.example.com";
  • X-Frame-Options - The highest possible score is 5. If you’re using the CSP header from the previous step, you’re also taking care of this line item at the same time, with the frame-ancestors setting.

Expert fixes

  • HTTP Strict Transport Security - The highest possible score is 5. HTTP Strict Transport Security, or “HSTS,” is a header that tells visitors’ browsers that your site should only be loaded over HTTPS, for a configurable period of time (usually set to a long duration, like a year).

    There are lots of excellent blog posts on how to set up HSTS (here’s one!), but obviously doing so requires you have SSL/TLS certificates for your server. This is easiest accomplished right now by using LetsEncrypt.

    Once you’ve got HTTPS working, you can implement HSTS. Start with a low duration and verify it works properly before increasing the duration. Be extremely careful—improper HSTS configuration might cause your site to become unreachable (at least until the max-age time period expires).

  • Redirection - The highest possible score is 0. Properly implementing HSTS will also take care of this line item.

  • HTTP Public Key Pinning - The highest possible score is 5. HTTP Public Key Pinning (HPKP) is a header that forces visiting browsers to check the fingerprint of your site’s HTTPS certificate against one of the fingerprints you provide in the header. If the fingerprints don’t match, the browser won’t load the site.

    As with HSTS, there are LOTS of blog posts on how to implement HPKP (here’s one!). Getting HPKP working with LetsEncrypt can be complicated, but it’s not hard (I’m using a method similar to the one described in this post. If you’re using Cloudflare or another CDN, your pinning setup will be complicated and you will likely have to pin at the root instead of the leaf.

    Getting HPKP working in a vacuum isn’t hard; getting HPKP working properly in production can be extremely difficult. And again, as with HSTS, making a mistake with HPKP can make your site unreachable, even by you.

Other things

There are plenty of other things to do to ensure you’re scoring as highly as possible on the list:

  • Enable IPv6, if possible.
  • Use an up-to-date cipher string for your SSL/TLS configuration—one that uses modern ciphers with perfect forward secrecy. If you don’t expect any legacy clients (i.e., folks on Android 3.x or Windows XP), disable SSLv3, TLS 1.0, and TLS 1.1 entirely and use only TLS 1.2 ciphers with perfect forward secrecy (the cipher string that works for me is:
ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:HIGH:!aNULL:!MD5:!3DES:!CAMELLIA:!AES128:!AES256-GCM-SHA384:!AES256-SHA:!AES256-SHA256
  • Keep Mastodon up to date—I use releaser to send me an email when a new tagged release appears on github.
  • Keep your Mastodon instance up! Uptime is a factor in the ratings.
  • Observatory includes this test, but it’s worth running the SSL Labs HTTPS scanner on your site so that you can view the full results rather than the Observatory’s summary. You should attempt to address, as soon as possible, any issues the scanner identifies.

That’s all I’ve got. Happy to answer questions, but a lot of “how do I?” type questions will depend very greatly on your specific setup. I can talk pretty intelligently about non-docker Mastodon with Nginx, Varnish Cache, and HAProxy (for SSL termination), but I don’t know a lot about implementation specifics on other platforms.

Edit - I apologize in advance for any errors in this post—it’s been a long day. Happy to correct anything that needs correcting.

5 Likes

this is really cool! thanks for the hard work!

Great recap!

Unfortunately Subresource Integrity is not in the latest version (1.4) because of webpack. There’s an issue about this. And I feel it’s not going to happen any time soon… If you’re brave enough you can still use SRI, but that implies you should update the hashes whenever there’s an update, manually. I didn’t find a better hack than using SriStatsPlugin to generate a JSON mapping file.

Regarding the ciphersuite, I’d rather use this one :

ECDHE+CHACHA20:ECDHE+AES

There’s an open discussion (can’t put the link due to the limitation, search for “Better TLS config” PR in tootsuite/documentation repo) but we all agree that the use of HIGH should be discouraged.

Cryptcheck (tls.imirhil.fr) will soon be updated and will check even more things : HPKP, TLSA, DNSSEC, OCSP (Stapling/Must-Staple), etc. It would be interesting to take a look at these security practices. I wrote a french article (could be translated in the future though) about how to do that : https://psychedeli.cat/configuration-https-2017/

3 Likes

It’s should be fine with the exception list I’ve got tacked onto the end to nix the problem ciphers—the string I posted (at least for now) gets full bars on the SSL Labs test. But on the whole I totally agree, yes.

From an instance scoring perspective, removing SRI isn’t necessarily problematic in and of itself as long as it’s something that affects all instances; like you said, though, it gives the more…patient admins potentially a leg up in the scoring stack by giving them something to tweak. It might potentially disrupt the ratings list a bit, which isn’t necessarily a real issue, though it does give admins a way to game the list a bit (though not necessarily in a bad way, since manual SRI implementation isn’t a bad security thing to have—just a pain in the ass).

Also very good to know about Cryptcheck. Checking all of those boxes (especially DNSSec, which isn’t 100% supported for some services like Route 53 on AWS) might end up being really complicated and should give us admins plenty to do to ensure full compliance.

Fun times!

Having the full bars on Qualys doesn’t mean it’s good. I cannot get them because :

  • I have AES 128 ciphers enabled (GCM mode only)
  • I use X25519 for ECDHE.

But AES-128-GCM is far more preferable than AES-256-CBC. Nowadays every modern webserver should support these ciphersuites, which are also the ones standardised in TLS 1.3 drafts :

  • AES-256-GCM-SHA384 (not sure why you exclude this one!)
  • AES-128-GCM-SHA256
  • CHACHA20-POLY1305-SHA256

X25519 won’t let me get 100/100 (but 90/100 instead) on the “key exchange” part, but it’s much better than using NIST curves. And using ECDHE is much better than using DHE.

So if I take your ciphersuite, I get this : https://paste.targaryen.house/?b1e9169656625b47#gYwlQJuAJn7GohjWse5Pq+hNsdc/WcpKlw4HMoBCQ9Q=
PSK should be really avoided, DSS too.

1 Like