Filen's password onion made me cry
Filen is a young German end-to-end encrypted cloud storage provider. Five years in, they are on the third version of their crypto protocol. The oldest accounts still authenticate with long-deprecated hash functions like MD2 and MD4, with attacks dating back to the last millennium. A downgrade attack could trick even recent accounts into using them, which for weak passwords could leak all files to the service provider.
This post walks through some of the cryptographic weaknesses I found and questions if end-to-end encryption is worth it when delivered on a webpage. I responsibly disclosed every finding to Filen and gave them a draft of this post to review before publication. You can find the unabridged list of findings here.
"Zero-knowledge encryption"
Many cloud providers market cypherpunk-style privacy as a service, and I call them cypher-SaaS. Filen is one of them, and its website promises the following.
Filen was built around zero-knowledge end-to-end encryption. Neither we, as a service provider, nor any other service agency will ever have access to your files
Such promises are hard to enforce cryptographically. ETH Zürich's Applied Cryptography Group found flaws in many of these products with zkae.io and brokencloudstorage.info. While moving to alternative providers in the spirit of di.day, I reviewed Proton, Internxt and now Filen, and found similar problems.
"Zero-knowledge encryption" has become a common marketing term for such products, but it is not a thing in the cryptographic literature. Zero-knowledge proofs exist, but they are a different primitive, presumably borrowed for the prestige. In practice it means confidentiality against the backend, so the server learns nothing about the contents of a user's files. Unlike other sites, the threat model here must therefore treat the backend as malicious. Whether that is realistic, especially in a browser, is a question for a later section.
MD5(MD4(MD2(password)))
End-to-end encrypted storage requires the user to be the sole owner of their keys, which usually means all keys are protected by the password. Normal websites receive the password in plain text and check it on the backend, but if Filen did that, it could decrypt every file. So Filen hashes the password client-side and sends only the hash. The first protocol version computed that hash roughly like this:
function hashPassword(password) {
return SHA512(SHA384(SHA256(SHA1(password)))) +
SHA512(MD5(MD4(MD2(password))))
}
This looks like every hash function the library supported squashed into one big onion-hash cocktail. While even legacy hashes like these cannot be reversed, if you have a target hash you can re-hash password guesses until one matches, which is why password hashing functions are deliberately slow. This onion is the opposite, incredibly efficient to compute, and that is the real issue. The output also concatenates two independent onions, so an attacker only needs the cheaper half to check a guess, which costs them less than it cost the client.
Filen is the party we need to protect the password from, yet it holds these hashes, so it could crack any v1 password. Luckily the hash was soon replaced by a proper password hashing function. The latest version uses argon2, which is deliberately slow and memory-hard. However, v1 is still supported for backward compatibility, and at login the client asks the server which hash version to use. So at any login the server can claim your account is v1 and harvest the weak hash. The researchers behind zkae.io showed similar downgrade attacks against Bitwarden and LastPass.
Encrypt like it's 1854
The cryptography that protects our privacy can be in direct tension with the usability features that cypher-SaaS may want to offer. Filen faced such an issue when they tried to solve server-side search over encrypted filenames. If you want to search for a file named banana.png and you start typing ba, they wanted a way to still perform a server-side search. That's why in the versions I reviewed the client split the filename into small chunks of length two or more. banana.png becomes ba, an, na, an, na, a., .p, pn, ng, ban, ana, nan, ana and so on. These chunks were then hashed with an HMAC and uploaded to the server. The HMAC is keyed with a user secret so the server cannot easily reverse it, but it is deterministic, so when you type ba it can be hashed to the same value which then allows for server-side search of that file.
This makes it sort of a "substitution cipher", and allows for statistical attacks, similar to breaking 1854's Playfair cipher which is no match for modern cryptanalysis.
Filen informed me that they are moving away from server-side search. Some clients still send the hashes but it's being phased out and the server does not process them anymore. There is research on searchable encryption, but for this use case I agree with Filen that client-side search is the most pragmatic approach.
Stranger keys
Filen lets you share files and folders with other users. When you do, the client encrypts the item's metadata, such as name and size, along with its encryption key to the recipient's public key. This is only secure if you authenticate that public key. Without that, a mallory-in-the-middle (MITM) can intercept everything meant for the recipient.
Filen does not authenticate the recipient's public key, a gap common in cypher-SaaS products. I found similar gaps in Proton and Internxt, and zkae.io found the same kind of issues in Bitwarden, LastPass, Dashlane and 1Password.
What makes it particularly worrying here is that the attack is not limited to the initial share. File uploads, moves, folder creation and renames all check with the backend whether the parent folder is shared, every time fetching an unauthenticated list of public keys and encrypting the item keys to them. So at any point a malicious backend could claim a folder is shared with a key it controls and gain access to any file you upload, rename or move.
Filen told me that they plan to introduce a trust on first use model and limit sharing to keys of the user's contacts in the future.
Crypto doesn't matter*
Should we even care about a product's cryptography if it's served as a webpage? If we don't trust the provider, how can we know they will serve us the correct cryptography in the first place? zkae.io argues that work like theirs or mine is still worthwhile:
Before going further, we note that there is a trivial attack against end-to-end encrypted applications that we wish to take out of consideration from the outset. Namely, a user can be supplied with a malicious client which, for example, simply returns the user's master password to the server in the clear. Such an attack would be detectable through code audits and – though possible via an obfuscated functionality – would be very risky for a vendor to conduct. This is especially true for signed software releases (e.g., desktop and mobile clients, browser extensions), as the existence of a signed malicious client would constitute proof of vendor misconduct.
As protocol specialists they reasonably scope this out of their threat model, but as much as I want to agree, I think that understates how feasible delivering bad code still is. It depends on how the software reaches you.
Websites are unsigned and re-delivered on every page load, so a vendor could serve a malicious version to only a few high-value targets (A), which is very hard to detect. Desktop apps are often no better. Many are Electron-based, which comes with a messy update ecosystem. Filen does not check software signatures when updating, and I reported the same issue to Proton, Bitwarden and Internxt. Such targeted attacks have happened before, as in the Notepad++ compromise, where an update channel without proper signature verification served malicious builds to only a small set of targets. In my experience most cypher-SaaS are served this way.
Signed software from a vendor-controlled channel (B), like Signal Desktop for Linux, still allows targeted attacks, but they are much riskier. A signed malicious build would prove vendor misconduct (or a compromise of the signing keys) if anyone caught it. I still prefer getting software through third-party distributors (C), such as installing KeePass through F-Droid. Malicious apps can still get in there, but the distributor attests that all users get the same version, so a malicious build reaches everyone at once, which is much noisier and quicker to detect. The Shai Hulud worm in npm, for example, was caught quickly because it hit so many people.
So the researcher's premise that a bad client is very risky for a vendor holds in (C) and, to a lesser degree, in (B). It falls apart in (A), which is how most cypher-SaaS are delivered. And the attacker does not even have to be the vendor. Whoever can intercept HTTPS can serve a malicious client, either with the private keys of eight eMudhra intermediates, which turned out to be recoverable from their public certificates, or by hijacking the DNS of three top-level domains to mint certificates for Google and other brands.
*if you cannot trust the code
I don't think this means web-based cryptography never makes sense. I personally prefer self-hosting, with OpenCloud for file storage and netbird for remote access, or magic-wormhole for sharing files between machines. If you really need a cloud solution, a cypher-SaaS may be reasonable choice, since its vendor has a commercial incentive to respect your privacy. Just be aware that a web-based cypher-SaaS could still read your data by serving you a modified client, a fact its marketing rarely mentions and sometimes even disputes.
Filen carries some bad legacy cryptography it cannot easily drop because of backward compatibility, but they responded quickly to my feedback and went through everything thoroughly. They promise to address many of the points I raised in their upcoming fourth protocol version, maybe even with a sprinkle of post-quantum security.