I have taken a couple terabytes of photos over the years, and have a mild phobia of deleting data, so I keep my photos on a Synology DiskStation NAS. I’ve got all the files on the thing:

because back in 2016, my apartment was broken into and thieves took all the electronics. I’ve been slightly paranoid ever since.

The Synology NAS generally works pretty well, but the interface for decrypting a drive after booting is extremely painful. It’s about 10 clicks, a username, two different passwords, and a UI that is kinda wonky (it’s a desktop in a web browser)? Here’s a screengrab:

22 seconds is my personal speed record.

There are alternative solutions to this – you can set it up so that it reads the encryption key from a USB disk on startup, for example – but, if I’m worried about the whole thing being stolen, it’s not a good idea to leave the encryption keys plugged into the thing, and it’s also not a nice UX to find and plug in a USB before turning the thing on.

This… seems kinda silly to me, and something that should be solvable with ✨ technology ✨. I’m only ever accessing this from one computer, for example, and every modern operating system has a good credential storage system (think Keychain Access on OSX or Credential Manager on Windows 10). So, maybe my computer could take care of supplying encryption keys to the DiskStation!

I went looking for options, and discovered that the DiskStation Software has a publicly-documented web API (pdf). At least, a mostly publicly documented web API – there’s no documentation for the methods to mount and unmount encrypted shares.

That web API is also used by the desktop software though! So it should theoretically be possible to use browser devtools to see exactly what is being sent to the DiskStation. 🤔

So, I clicked on all the right things in the Web UI, got to the bit where you type in the password, opened the devtools to the “Network Requests” tab, typed in abc123 as my password, hit “send” and then watched for the requests:

Firefox devtools screenshot, showing a request to an ominously-named 'encryption.cgi'


Hmmm, that encryption.cgi looks a little ominous. I wonder what’s in it?

Firefox devtools screenshot, showing huge blob of base16-encoded public-key

Yep, looks like encryption, alright

Ok, a public key. And then the web interface loads a spinner, and makes a request to entry.cgi. Let’s take a look at the contents of that request:

A devtools screenshot, showing a jumbled blob of base64-encoded data with the phrases 'rsa' and 'aes' sprinkled throughout.


Ok, so if we want to send a magic request to mount the encrypted share, it’s not going to be enough to supply the encryption password; we’re also going to need to figure out how to correctly supply it – how to encrypt the encryption password, so to speak.

So, what is this crypto system?

Right. Here’s a high-level overview:

Still with me? It’s ok if that didn’t all make loads of sense yet; we’ll now go over the interesting bits more closely.

Symmetric / Asymmetric Ciphers

Fundamentally, we’re using two cryptographic algorithms in concert:

It seems weird that we’re using both of these at the same time, right? But both systems have weaknesses, and mixing them together is an attempt at minimising the disadvantages of both schemes.

The problem with symmetric crypto systems by themselves is that you need a way to get that one key to every party that needs it. But you can’t just send it to them, because you don’t have a secure channel yet! Your options are:

Asymmetric crypto doesn’t have this problem – if you’re able to share a key that can only be used for encrypting and not for decrypting, then the decryption keys never have to leave the recipient machine. You can then encrypt some text, and nobody except for the recipient can decrypt it – not even you! Put it on the internet, write it on a banner attached to the back of a propeller plane – the message will only be readable by the intended recipient.

A propeller plane with a banner reading 'your ciphertext here'

Asymmetric cryptography is slow, though, so doing it for lots of data is going to really hurt performance.

A common solution is to simply make up a symmetric key, perform some symmetric encryption using that key, then encrypt that key using asymmetric cryptography, and deliver both to the remote party. Then, the remote party can:

That’s what this particular system is doing. ✨

A quick note about RSA

I don’t know much about how RSA works, but as part of researching this article I discovered that it’s pretty easy to implement badly, and extremely difficult to implement well.

Two developers correctly implement RSA, 2010, colorised

Two developers correctly implement RSA, 2010, colorised

As software developers, we spend a lot of time shipping things once they get to a point of “it looks like it works”. You can’t take that approach with cryptography, and because everything is so opaque, it’s a lot easier to declare that your implementation works without realising that it’s missing fundamentals. Like correctness, for example.

Let’s talk about random keys2

Recall that this algorithm is generating 501 random bytes from a 77-character set, and encrypting that with RSA. 501 seemed like an oddly specific number to me; here’s the specific set of circumstances that characterise why we’re generating a string of this length.

RSA is a block cipher, which means it operates on distinct fixed-size blocks of input and produces fixed-size blocks of output. How big can each block be? Well, RSA public keys are made up of two numbers: a (very large) modulus n, and a much smaller exponent e. RSA is capable of encrypting any message that’s smaller than n if you convert it to a number. In our case, we said that our modulus was a seemingly-random 4096-bit integer – that means (approximately) that we can encrypt 512-byte blocks.

There’s an important implication here: if you don’t have enough input for a block, you need to fill it up somehow. Normally this is called a padding scheme3 in block-based cryptography.

Now, the system on this NAS in particular uses PKCS1-v1.5 padding, which prepends the message (before encryption) with 3 bytes of headers plus a minimum of 8 bytes4 of random garbage. That makes 11 bytes out of a 512 byte block, which means we’ve got… 501 bytes left for our message. ✨

Lo-fi key derivation

So… we’ve got this 501-byte string, with each character being one of 77 possibilities, and we think we know why it was chosen to be 501 bytes long. But that’s not going to work for AES-encrypting the password:

These constraints need to be managed. We can smooth over both by applying a key derivation function, based on repeated rounds of MD5 hashing and concatenating. Hash algorithms, in general, are good for this because they map input data to… not random, but random-looking output data.

I like to think of hashing as “entropy laundering” – if you’ve got 256 bits of entropy in a 16kB file, taking the first 256 bits will give you only 64 bits of entropy. If you hash the whole 16kB file, then the entropy goes back up to the maximum of the entropy in the file and the entropy provided by the hash algorithm. This works because hash algorithms are designed to exhibit an avalanche effect.

I do not have the tools or experience to evaluate whether this specific scheme is secure or not (this one seems esoteric, and the use of MD5 is scary!)… so let’s move briskly on.


Finally, after a bunch of outdated and somewhat esoteric primitives, we’re back to something that is good and works and only slightly a loaded footgun and is still recommended for widespread use! It’s AES with a 256-bit key.

AES is our symmetric cipher. Like RSA, it operates on blocks of fixed length. Unlike RSA, these blocks can be chained together. The strategies to do this are called “Block cipher modes of operation”, and AES implementations ship with a number of them. The problem is, this means you have access to bad options as well as better options, which makes it easy to choose the wrong thing. Synology’s code uses Cipher Block Chaining, or CBC, which… unfortunately is pretty susceptible to a number of attacks, the classic being a padding oracle.

If you’re using AES for any of your own stuff:

What does this encryption scheme actually protect against?

That’s all a lot of rigmarole, but what does this scheme actually protect against? It’s helpful to consider the device in context here:

The problem is, you administer the device over an HTTP interface, and you can’t easily run HTTPS on local networks, because you can’t give the NAS a canonical hostname, and so it’s hard to get an HTTPS certificate that’s trusted by your computer’s default settings. So, Synology have opted to support doing everything over HTTP.

HTTP on a trusted network isn’t terrible, but as a device manufacturer, Synology have no guarantee that the network the device is running on is trusted! For example, if you’ve got one of these NAS boxes running on a passwordless wireless network, it’s trivial to intercept unencrypted HTTP traffic. In these specific situations, having a shonky encryption scheme is better than nothing – it prevents the password from being read by any ol’ eavesdropper sitting on the bench across the street.

It’s worth noting that all of this encryption is moot if someone is able to put another computer on your network that’s pretending to be your NAS. In technical terms, you’ve got no way of authenticating that what your computer is talking to is actually your NAS! So it’s relatively straightforward to mount a muppet-in-the-middle attack and steal passwords in that manner.

Muppet-in-the-Middle attack

Muppet-in-the-Middle attack
Apple, via ‘Campaigns of the World’

Fundamentally, this is hard because the security of the passwords you’re sending to your Synology NAS depends entirely on how you’re using the device, and on how its host network is configured. Synology is in the difficult position of trying to make sure that people don’t have their NAS credentials compromised without actually being in a position to control this particularly well.

Alternative: just use SSH

In the end, I decided not to use this API for my original project – my ideal scenario is that I’d be able to mount the encrypted shares automatically if they were locked, and if I’m automatically attempting to send my passwords across the network, I really want to be sure that my computer can authenticate the NAS. I could theoretically do that by building a method to mark my home network as trusted, but by this point, it seems simpler to pick an alternative approach.

Turns out the Synology NAS also supports SSH, and you can mount encrypted shares over SSH. I’ll probably do that instead, because SSH:

That’s it!

I wrote this blog post as a consolation prize for doing a whole lot of research and reverse-engineering that I’m not going to use after all. 😅  But I learned a little about cryptographic principles from examining this system, and hopefully you have from reading this article, too! If I’ve said something egregiously wrong or you want to commiserate about esoteric encryption systems in consumer devices, feel free to get in touch 📧.

  1. Crypto twitter like, the cryptography people, not the Bitcoiners. ↩︎

  2. 🎵 let’s talk about you and me 🎵 ↩︎

  3. It turns out that having padding is security-critical for RSA, which is slightly terrifying. Nate Lawson argues that padding is entirely the wrong name, and it should be called armouring instead, and Cryptopals have an exercise to teach you how to break unarmoured RSA. ↩︎

  4. From the RSAES-PKCS1-v1_5 specification,

    The padding string […] is at least eight octets long, which is a security condition for public-key operations that makes it difficult for an attacker to recover data by trying all possible encryption blocks.

    I haven’t tried this, but it sounds fun? ↩︎

  5. 1/(77/255)^32, assuming 256-bit keys. ↩︎

  6. The idea of putting my Synology NAS on the public internet gives me the heebie-jeebies: these things run loads of software on loads of ports, and I get the vibe that they’re easy to misconfigure and open up to attack. If you want to access your NAS from the internet, put it behind a firewall and set up Tailscale↩︎