How Safe Are They?
I wanted to talk briefly about this as a lot of people don't believe browsers have strong password safes, that they can be retrieved in plaintext far easier than something like KeePass could, and that you're essentially giving away your passwords if there's ever a browser attack that lets bad actors steal files.
How does Firefox store passwords?
Let's check out Firefox. Firefox stores passwords in two files according to their documentation. That is the key4.db file and the logins.json file. The logins file contains a list of encrypted usernames and encrypted passwords along with the url they correspond to and some other details like time created and time last used. The encryption of the details is done with an AES-256-GCM cipher, according to this Firefox update from 2019.
AES-256 is a tried and trusted format that is regarded as quantum resistant. Grover's algorithm will only reduce symmetric key algorithms by half their strength as opposed to asymmetric algorithms which get destroyed entirely by Shor. So AES-256 goes down to 128 - which is still relatively secure.
Before we get into some math, let's take a brief moment to review. GCM is an authenticated encryption mode of operation. It takes a key, unique IV, data to be processed with only the authentication, and data to be both processed with both authentication and encryption. It outputs the encrypted data of part 4 and an authentication TAG. The TAG is used to to verify that the encrypted data or associated data has not been tampered with. AES uses GCM so as to to get better overall performance as compared to CBC since CBC cannot run in parallel. With GCM, each block is conceptualized and encrypted with AES in parallel. AES-GCM is known as an AEAD form of encryption. This means that it simultaneously assures both the confidentiality and authenticity of the data. A common attack on AES-GCM is message forgery given a reused nonce, and this is called the forbidden-attack.
Here's a quick example from the link, math courtesy of ashutosh1206. Take message g1, encrypted using g1(X) = C1,1X2 + L1X + S and message g2 encrypted the same way, g2(X) = C2,1X2 + L2X + S. L = len(A) || len(C) where A is the associated message (recall this from our GCM review?) and C is the ciphertext. S is our nonce value and, since each message uses the same nonce, the S values will be the same. The above polynomials cannot be solved without first removing S from them. Since we know that g1(H) = T (H is the authentication key and T is the authentication tag which includes the nonce value (S) in it), we can add T to both equations and then add the two polynomials together. Doing so, we get f(X) = (C1,1+C2,1)X2 + (L1 + L2)X + (T1 + T2) and solving this quadratic equation gives all possible values of H, which we can then use to generate new and valid authentication tags and decrypt past data with.
Looking further at Firefox's implementation they, to the best of my knowledge, follow the RFC for AES-GCM so they don't use repeating nonce/IV values. Given this, attacking the implementation of encryption is impractical.
Moving onto other vectors, we can take a look inside key4.db which gives us two tables.
The first is meta data and yields us two rows and columns. For the "password" row, one column is for the salt and the other is for the ASN.1, which just stands for Abstract Syntax Notation 1. We'll need this for analyzing the data in the next table, nssPrivate, which holds our master key.
nssPrivate has a ton of columns and each has one row. Inside a11, we can find the master key used for decrypting the rest of the data in logins.json and which the tools we'll look at below automate extracting. One way or another, each of these tools uses the salt and ASN.1 to format and retrieve the key value in a11 row[0] and uses this to decrypt the information seen in logins.json.
What tools are there to dump this information?
There's a couple of tools like Dumpzilla and firefox_decrypt.py. Dumpzilla doesn't work anymore because it requires the signons.sqlite file, which is no longer used. Ah, but firefox_decrypt.py also looks at the logins.json file! If we run this on the logins.json file alongside the key4.db, and there is no master password set, we get back the passwords stored. But if a master password is set, there's no bruteforce functionality and the program fails.
A lot of tools try to primarily use Firefox's NSS library - which stands for simply Network Security Services. But one tool, firepwd, uses none of that and does it all their own way. So props to them for doing a custom implementation.
What about KeePass?
Keepass is very forthcoming about how they secure your information. In 2.x versions, you get the option of AES or ChaCha20, both with 256 bit key sizes. AES is much more commonly seen than ChaCha20 so out of the two, let's check out ChaCha.
ChaCha20 is the successor to Salsa20. It's been adopted by Google as the defacto encryption scheme for their QUIC protocol and KeePass uses it in conjunction with CBC and HMAC-SHA-256 to ensure the datas' authenticity and integrity. This has the drawback though of being slow, which is why TLS has moved to ChaCha20-Poly1305. In something like a password safe though this slowness would be negligible as you only have to enter your password once to open up the safe. You're not doing repeated encryption operations thousands of times a second, it's only one, so normal users will never experience this. This could affect attackers though who trying to brute-force the master password. What will also affect attackers is KeePass' option to use Argon2 for the KDF. This function has resistance against GPU and ASIC attacks built into it through increased RAM and CPU costs which makes it harder to derive a valid key without knowing the password. The use of HAMC-SHA-256 to provide authentication is a move to prevent chosen-ciphertext attacks. This method is called encrypt-then-MAC and allows the decryptor to check the message authentication code before they decrypt the message. If the MAC isn't valid, no decryption occurs. The combination of a slow encryption scheme through CBC, using Argon2 for a KDF, and hashing with HMAC-SHA-256 seems like a very strong approach to dissuade brute-forcing and oracle attacks.
Overall, KeePass has very strong encryption and does a great job at protecting their users. I do wish that Argon2 was the default, but regardless attacking it is impractical. There are more things they do to protect user information that I haven't covered in this portion, like their secure desktop and in memory encryption, so read their full write up if you would like additional details.
What tools are there to dump KeePass kdbx files?
None. KeePass requires a master password by default so is not susceptible to the same attack that Firefox is.
How do they compare?
If you set a master password in Firefox then no one will be able to retrieve your passwords except for those with that master key. The encryption chosen is as good as other desktop based password safes. Yes, without a master password set, Firefoxs database can be retrieved and information stolen trivially. But if you take one moment to setup a password, then you foil the attackers. Some may be wondering "What about Chrome?" Well Chrome secures your safe with your Windows session making it trivial for an attacker to read it if you're logged in.