I thought Mastodon was already secure?

Well, it depends. You see, a Mastodon instance is just a web server, like any other on the internet. It's only as secure as how the storage is locked down and more importantly; how the transport layer and presentation to the user is locked down.

Further: security ≠ privacy. Privacy is the guarantee that only the creator of some content and the intended receipients of that content can access it; no one else. Security is ensuring that the platform itself cannot be tampered with by an actor with ill intent. A public toot on Mastodon is by no means private. And a webserver who's admin password is password is hardly secure.

What's Transport Security?

This is HTTPS and WSS the two protocals that transport bits from the Fediverse and your Mastodon instance to your computer and your phone.

Why is tight transport security important?

This is the reason: img

This was Room 641A, a splitter closet at the AT&T switching center in San Francisco. Fibre optic splitters handling backbone internet traffic flowing in and out of the building went into this room. From there, the National Security Agency had installed NARUS monitoring equipment to peek into the data flow.

Unencrypted data.

Unencrypted data that can be easily modified in-flight.

This and other revelations of large-scale traffic snooping has been met with a simple response: let's encrypt everything. This renders traffic sniffing completely useless.

Achieving a high security score

Ever since the “encrypt everything” movement took hold, more attention has been paid to questions like “how secure is it?” There are two great scanner tools equipped to answer that question and you should use them when setting up any website on the Internet, not just a Mastodon server.

It should be common knowledge by now that the early form of web security that SSL is no longer secure. We have all moved on to the new standard, TLS (Transport Layer Security), specifically version 1.2 at the time of this writing.

With that security comes the strength of the digital certificates used to handshake connections, authenticate connections and to carry out encrypting strings.

A quick look at https://mastodon.social with CryptCheck and we can see some flaws:


While this is “okay” security, the web server for Mastodon's flagship instance offers some interesting encryption choices; some of them are not ideal. The best ones are listed in blue. For Mastodon's flagship instance the offering of ciphers that have no Perfect Forward Secrecy is concerning. PFS is basically a re-negotiation of a new public/private session key that's generated at random when the connection opens. This makes it worthless to record Internet traffic, as even if you got a hold of a server's private key, you still have no idea how to decrypt the packets.

Now let's take a look at my instance:


A perfect score. I'm offering fewer digital signature and encryption ciphering methods, but the ones I am offering are evaluated to be ‘more secure’.

To top it all off, I'm using just 384 bits for key sizes rather than a 4096-bit RSA certificate. How's that possible?

Let's get secure!

I'm going to talk about the hardest part of TLS configuration first and then move on to the easy stuff last. The hardest part is getting a perfect encryption cipher score on CryptCheck and Qualys.

To get a perfect cipher score on both TLS scanning tools you need to fully embrace elliptic curve certificates.

Normal key exchange vs. ellipcal curve key exchange

In the last couple of years there has been some growth of support for ECDSA: ellipcal curve digital signature algorythm. All modern browsers support it and all modern software stacks do too, uncluding OpenSSL and GnuTLS.

If you're familiar with how public key cryptography works under RSA, elliptical curve cryptography works exactly the same. The only real difference between the two is the type of math problem that's computed to arrive at the public and private keys used to sign documents and encrypt traffic. Traditional RSA encryption involves the factorization of large prime numbers–a problem which has no easy formulaic way to solve.

Elliptical curve cryptography uses a different math problem that's actually much simpler to understand and far easier to compute. It also uses much shorter keys for the same relative security strength as the traditional RSA approach.

In fact, let's take a look at what the key sizes are for the same encryption strength between EC and RSA:

RSA and Diffie-Hellman Key Size Elliptic Curve Key Size
1024 160
2048 224
3072 256
7680 384
15360 521

CryptCheck's recommended EC key size is 384 bits, which would be the equivalent of a 7680-bit RSA key! To get a “blue” ranking for an RSA Diffie-Hellman key you'll need to use a 4096 bit encryption key. Let'sEncrypt only gives you a 2048-bit keyed certificate out of the box.

Let's Encrypt supports ECDSA, sort of

To do ellipcal curve handshakes when users come to your Mastodon instance you will need to replace your default 2048-bit RSA certificate you got from Let's Encrypt with a new EC certificate. The trouble with EC certificates right now with Let's Encrypt is that the certbot auto-renewal tool will not work on them. You will need to build a script to regenerate a new EC certificate and use cron or some other job scheduling tool to periodically replace the certificate. If you're cool with that, let's continue.

First, we need a private key

We need to generate a private secret key which we'll use to build a Certificate Signing Request.

openssl ecparam -genkey -name secp384r1 | openssl ec -out ec.key

Now, we need to build a Certificate Signing Request file

openssl req -new -sha256 -key ec.key -nodes -out ec.csr -outform pem

These created two files, ec.key which is your private key and ec.csr which you will be sending to Let's Encrypt to get your shiny elliptical curve certificate for your web server.

Now it's time to get the certificate

This will call up Let's Encrypt and if everything goes well, you'll get your ECDSA certificate:

certbot certonly --nginx -d {your_domain} --email "{your_email}" --csr ./ec.csr --agree-tos

This will build several files, but 0001_chain.pem will be your certificate in full-chain form.

Install the certificate

This will of course vary for your server, but here's a copy of my entries in nginx.conf

ssl_certificate /etc/letsencrypt/0001_chain.pem; ssl_certificate_key /etc/letsencrypt/ec.key;

Remind NGINX to use the correct curve

Since 256 bit curves don't get you a 100% rating on CryptCheck, you need to edit your nginx.conf and remind your server listening on port 443 to select the correct curve for key change.

Add this in your SSL section of nginx.conf:

ssl_ecdh_curve secp384r1;

Setup certificate renewal

Like I said before Let's Encrypt's cerbot utility doesn't know how to renew elliptical curve certificates. But the process is fairly trivial if you want to keep your private key and your original Certificate Signing Request file.

Setup a bash script under /etc/letsencrypt and let's call it renew.sh


cd /tmp/certrenew rm /tmp/certrenew/*

certbot certonly --nginx -d {your mastodon domain name here} --email "{your email address here}" --csr /etc/letsencrypt/ec.csr --agree-tos\ --non-interactive --cert-path /tmp/certrenew/cert_ecdsa.pem --fullchain-path /tmp/certrenew/fullchain_ecdsa.pem

cp -R /tmp/certrenew/. /etc/letsencrypt/.

service nginx restart

Follow that up with these two commands (assuming you're rooted):

mkdir /tmp/certrenew chmod +x /etc/letsencrypt/renew.sh

You should be able to run this script manually to make sure it executes and deposits the new .pem files into your /etc/letsencrypt directory. Make adjustments to your nginx.conf file so your fullchain certificate is now pointing to fullchain_ecdsa.pem You should notice when logging in/out of Mastodon that you now have a brand new certificate.

Now it's a question of scheduling renew.sh to run. Let's Encrypt certificates run for three months by default, so a once-a-month routine in crontab should be fine.

sudo crontab -e to get to get to your scheduled tasks for the root user and schedule the script to run monthly.

# Renew the SSL certificate at 3AM on the 9th day of every month 0 3 9 */1 * /etc/letsencrypt/renew.sh

Yeah, it's not perfectly ideal, but if you want to do something better than this feel free.

Make sure to use AES 256, not AES 128

Another mistake that will cause you to miss getting a perfect score is allowing encryption with 128-bit AES. Whatever your opinion is on saving power consumption vs. getting a perfect score; if you want a perfect score you need to use AES 256-bit encryption.

In NGINX it's very simple to mandate AES256:

ssl_ciphers 'AES256+EECDH:AES256+EDH:!aNULL'; ssl_prefer_server_ciphers on;

At this point you should be getting a perfect CryptCheck cipher score.

Getting a perfect Qualys SSL Labs Score

Once you've taken care of elevating the security of which cipher you're using and switching to elliptical curve DSA rather than the default RSA certificate that Let's Encrypt gives you, there's a few more items you need to take care of to get the best score possible on Qualys.

The rest of these tips are simple changes in nginx.conf and to your DNS records.

Content Security Policy

This is the most annoying one of the bunch. This is a custom HTTP header that the browser cartel honors. It instructs the browser as to what content that the HTML page loads is illegal.

Without going into too much detail I'll just show you what my CSP looks like:

add_header Content-Security-Policy "default-src 'none'; font-src 'self'; media-src 'self'; style-src 'self' 'unsafe-inline'; script-src 'self'; img-src 'self' data: blob:; connect-src 'self' wss://innerwebs.social blob:; frame-ancestors 'none'; frame-src https://*.youtube.com ";

The only part you need to adjust is the exception wss:// which allows WebSockets connections to your Mastodon instance, to point to your own instance. I am also allowing YouTube share frames to load so YouTube videos can be played inside Mastodon without having to navigate away from the site.

Unfortunately the way Mastodon is written there is some inline Javascript code in the app, so if you want Mastodon to work correctly you will need to add an unsafe-inline exception under style-src.

Referrer Policy

You should always enable this:

add_header Referrer-Policy "no-referrer";

This will instruct web browsers to not send a Referrer header when visiting links from your Mastodon instance pointing to other web pages. That makes it much harder for companies like Twitter or Facebook to do analytics and trace traffic back to your Mastodon instance, especially if someone pastes a link to a tweet.

Make sure to set a CAA record at your DNS provider


Almost everyone is using Let's Encrypt but we all forget to set a CAA record on our websites we setup, which is such an easy win.

The CAA record is a simple way to thwart spoofing. If someone did manage to compromise your web server instance but not your DNS host, clients will refuse to connect if they see a certificate that came from somewhere else than Let's Encrypt.

Strict Transport Security

Also known as HSTS, this is a header that commands a web browser to remember that your site can only be accessed via HTTPS, not HTTP. Most of the time a man-in-the-middle attack is going to attempt to redirect someone to a non-HTTPS web server. Even if this does happen, chances are good that this won't happen on the first visit to the site but on some subsequent visit; therefore the browser will reject the request when HTTPS cannot be attained or the site redirects away from HTTPS.

All you have to add in your nginx.conf is this line:

add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;

Increase SSL session cache

It's generally safe to reuse SSL session parameters for a while, particularly for servers that service requests from mobile phones that drop out frequently. Reneogiating new TLS authentication can drain some CPU out of the server. To avoid that, do this:

ssl_session_timeout 60m; ssl_session_cache shared:SSL:20m;

This enables a shared cache that will hang on to SSL session information that's shared between NGINX workers. A 20MB cache can hold about 80,000 sessions—so that's more than enough for most Mastodon instances except the biggest.

That's it!

As you work through your Mastodon server's settings I encourage you to look at the results from my Qualys SSL Labs test and compare them with your own server. Not only should you aim for an A+ rating but you should aim for 100% in all four categories to ensure your security is at the highest possible standard for Mastodon.

Perfection: img