A secure password manager or shoot the user in the knee
Introduction
Hi all!
I’ll say right away: I’m not going to claim the title of the smartest, I just want to express a number of my thoughts, which are IMHO. If you don’t agree with something, please comment and let’s discuss it.
This article will be divided into two parts. In the first, I'll look at some popular password manager solutions and tell you what I don't like about them and why I decided to come up with something new. The second contains the most secure manager, in my opinion, a password manager, as well as a description of the algorithms that I will use to implement it. By “secure” I mean the following: everything must be done to ensure that no one can gain access to the user's passwords, even if it means denying the user access to his passwords.
Popular password managers
1) KeepPass – this password manager comes first in almost all reviews. But at the same time, in my opinion, it has problems, the main ones being:
Firstly, all data is stored locally. Some may say that this is not a problem, because the data is encrypted and a password is required to access it. But in my opinion, storing data locally is the biggest mistake, even in encrypted form: for example, in Windows, you can steal almost any file stored on the system, and when using a master password, few will make it too complex, because then it will have to be stored somewhere else.
Secondly, what is even worse in my opinion and was partially covered in the previous paragraph: to access passwords you need… A password. Some kind of recursion is obtained.
Thirdly, it displays all logins for all services, data for which is stored in the manager. Convenient, but in my opinion it can simplify the process of accessing a user's account even if they have changed their password. A simple example: an attacker gained access to an old version of a file and was able to decrypt it; all user names and passwords are available in the database, so the attacker only needs to try passwords on other services, in case the user simply uses the same passwords. Of course, hiding the full list of services and logins is a shot in the foot of the user, but look at the title of the article.
2) Bitwarden – a cloud password manager whose server can be deployed locally. Everything about it seems to be perfect: end-to-end encryption, two-factor authentication. But there are three “buts”:
The first and second “but” are exactly the same as in the second and third points of the previous manager, so I will not repeat myself.
The third “but” relates to the fact that you can easily access passwords from various devices, and not just from the one from which the passwords were created. Yes, you can set up 2FA (which is optional), but what if the Bitwarden database itself is leaked? Depends on the type of 2FA, but in the case of, for example, authenticator applications (for example Google Authentificator), until you change the 2FA server, your account may only be protected by a master password.
So what do I consider a truly secure password manager?
Now a utopian situation will be told, with which someone will laugh, someone will cry, someone will say that I’m crazy, but let’s get started:
1) Storing passwords in the cloud, and passwords are protected end-to-end encryption.
2) The password can only be accessed from the same device from which it was created
3) Lack of master passwords for logging into the system.
4) Login must be done Necessarily Using two-factor authentication algorithms, ideally TOTP, your email can also be hacked, and it is still more difficult to gain access to two devices at the same time.
5) The password manager should not reveal the list of your services and logins for them.
6) The password manager should not be able to restore access to a user account in the event of loss of access data. Otherwise, an attacker could use this to gain access to passwords by recovering login information.
6) The password manager should not know which password entry in the database belongs to which user (optional). What I mean here is that even after going through all the circles of hell, draining the database, decrypting it and the passwords stored in it, it was impossible to identify the account to which the password belongs, or the user who owns it.
This sounds like a shot in the foot to the user, considering that a device can only be reliably identified by its hardware, which means that a user using such a password manager will lose access to passwords in the event, conditionally, of a hard drive failure. Also, failure to recover login data will not have a positive impact on the user's experience of working with this manager.
Algorithms for implementing a password manager
Now I will propose several algorithm ideas to make the implementation of such a manager possible. To begin with, I will introduce three definitions:
1) Hardware info – a string containing data about the device (such as the name of the processor, video card, amount of RAM, serial numbers of drives, etc.)
2) Hardware key – a key generated based on device data. For example, you can calculate it like this: SHA512(Hardware info)
3) Software key – information about the current version of the client software (for example, checksum of application files)
Knowing these three basic definitions, you can go directly to the algorithms themselves.
1) User (device) registration:
a) The hardware key of the device is sent to the server.
b) The server generates a random 512-bit sequence (regSalt).
c) The server calculates totpKey = SHA512(hardware key | regSalt) and enters it into the database for 2FA.
d) The server returns totpKey to the client, which the user must store securely somewhere in order to generate one-time login codes in the future.
2) User (device) authentication:
a) The user enters a one-time code, which is sent to the server along with the hardware key
b) The server searches the database for this hardware key and compares the current value of the one-time code (totp) for totpKey with the one received from the client.
c) If the one-time codes match, the server returns two JWT tokens to the client: one for performing actions with passwords, the second for re-authentication after the first token expires.
In points 1 and 2, authentication is implemented in such a way that the user does not need to have a master key to authenticate in the service.
3) Creating/editing a password on the server:
Due to a number of features of the method I proposed for storing account data, I had to make some complications in the algorithm, so it’s worth paying closer attention to hands words.
The server checks the validity of the token for all incoming requests in this block.
1) The client sends to the server information about the service (name) for which a password is obtained.
2) The server returns a salt belonging to this service (a pseudo-random 512-bit value generated at the time of registration of the service, i.e. having a constant value).
3) The client calculates the password identifier as follows (login is the user’s login on the service):
4) The client generates a module for the RSA2048 algorithm based on the password ID (the module is known to the server because it can do all the same things for the password ID):
, where num are sequentially generated numbers (sequences of bits). The product of the first two distinct prime numbers will be the modulus.
5) The client calculates the public exponent e (which is known only to him):
a) The value on the basis of which the open exponent will be generated is calculated:
Note that hardware info, as well as login, are unknown to the server.
b) The open exponent e is calculated:
c) If GCD(e, (p-1)*(q-1)) != 1, take the next 2048 bits generated until GCD becomes 1.
6) The encrypted password is calculated:
7) The additional open base totpE is calculated (let me remind you that totp is stored in encrypted form in the token):
Then totpE is calculated, similar to the calculation of the open base, which only the client knows.
8) A triple is sent to the server:
The first two fields are necessary to identify the password and store it, the last one is to prevent older versions of the manager from interacting with passwords than the one with which the password was created.
9) The server receives a triple of data, calculates totpD such that:
And calculates the encrypted password:
After this, the server enters the three into the database:
Additional encryption is necessary in order to guarantee protection against a MITM attack in case an attacker has access to hardware info (for example, if the user's computer is infected): passwords are additionally encrypted using RSA with the same module, but with different open and closed exhibitors, which are generated using a one-time code that was entered when logging in for a given session. To make it impossible to quickly enumerate all open exponents for each totpKey value (and there are only a million of them), the PBKDF2 algorithm is used, which greatly slows down the speed of key enumeration.
But there is a problem with using PBKDF2: on average, it takes 2-4 seconds to generate one pair of p and q using this algorithm. Accordingly, this is another shot in the user’s knee: you can wait for the moment of creating/receiving a password for a few of these 3-5 seconds, provided that the key is generated on the client while waiting for a response from the server.
4) Receiving a password from the server:
In some ways, to obtain a password from the server, all the same steps are performed as to create or edit it.
Let me remind you that in this case the server always checks the validity of the token for incoming requests (authorizes the user)
1) The client sends a request to the server specifying the password ID.
2) The server searches the database for a record with this identifier. If no record is found, returns Not Found, otherwise goes to the next step.
3) The server generates a key for additional encryption using RSA. The modulus is calculated in the same way as in the previous section, and the open exponent is calculated as follows:
Then it calculates the open exponent using PBKDF2 in the same way as in the previous paragraph.
4) The server sends the following pair of data to the client:
where software key is information about the version of the program with which this password was created in the database (shows the client the need to update the password in the database if it was created using an earlier version of the program).
5) The client calculates the modulus, open base and totpE (the client has totp stored in RAM after successful authentication on the server). The client then computes a closed base d such that:
6) The client calculates the password by raising the resulting double-encrypted value to the power d:
7) If the password was created by an earlier version of the program, the client updates the password on the server.
What's next?
In the future, I am going to put this system together by writing a server in ASP.NET Core and a desktop application in WPF, and then release them into open-source. The server is mostly completed, the next part will be devoted to it.
Conclusion
Thus we successfully shot the user in the knee created algorithms that will help counter a large number of attacks. I will give the main ones:
1) Difficulty in accessing authentication for an outside user (of course, we cannot talk about complete impossibility, but in order to authenticate in the system you must have access to either the server or simultaneously to the user device and the authenticator device)
2) Difficulty for an attacker to obtain the password in case of successful authentication. To obtain the password, it is necessary to have access to the user's device, otherwise decrypting the password will be quite problematic (the server does not know the public/private exponent of the key). Therefore, coupled with the first factor, an attacker needs access to two out of three devices in the system to obtain the password. The offender also needs to know the name of the service for which a password is needed, and the user login for this service.
3) Complicating the man-in-the-middle attack. This depends on the attacker’s ability to access the user’s device: if there is access to it (for example, there is malware that receives data about the device), then additional encryption using totp helps protect the password in the event that only the message with the password is disclosed. If the intruder sees all the traffic between the user’s device and the server, then the measures taken are not sufficient: both the one-time totp code and the password ID will be known to the intruder.
4) Protection against receiving passwords by the server owner: despite the fact that the server knows the RSA key module, it does not know the public or private exponent, so it is not possible to decrypt the password.
5) Protection against receiving a list of services and user logins. As stated in the text of the article, this is impossible for two reasons: passwords are stored not tied to a specific user. Information about the login and service for a specific service is only part of the hashed data, so even knowing the password identifier it is not possible to obtain the former.
But at the same time, this system is unlikely to be useful to ordinary users, because if the device configuration is changed, passwords will disappear into oblivion. I see the main application, for example, in companies where equipment updates are rare, and I propose to store only passwords that, firstly, can be quickly recovered, and secondly, really important passwords that need to be protected. Otherwise, in the event of a failure of some part of the device, the user will have to recover/remember all passwords.