Creating a client-side encryption system
Nowadays there are lots of cool online services that allow you to express yourself or create almost anything that may come to your mind. Many of them are apparently free, the only thing you need to give away for using them is your personal information.
Some companies trade that data to make money, others just keep it to offer you a better tool. It seems that we always need to give at least our email address to use an online service. Is it impossible to create an online app without knowing anything from the users?
No, it’s not impossible. There is one kind of apps called Zero Knowledge applications, that doesn’t need the user to provide any personal information in order to create an account. The key is encrypting the data in the client side in a way that the backend can’t access to the information to be stored.
In this article we are going to explain how a Zero Knowledge app would work in a simple way, for every developer, there is no need to be a cryptography expert to understand it. It will be a cloud system to store data from users and we won’t even know their usernames or email address.
Creating the backend
We want to store user data, but we don’t want to know what data is that. To be sure, our system must make impossible that we, or anybody else but the owner, can access to that information.
The way of taking our dirty hands out of user’s data is a good encryption. One of those considered secure, impossible to decrypt by brute force in a million years.
We can’t let our servers encrypt the data because we don’t want to have anything to do with that data apart from storing and returning it to the user. So it’s the user the one that needs to encrypt it and send it to us in a format that we can’t read. The data needs to be encrypted in the client side.
This is exactly the way the PassPill Project wants to store the user’s passwords. For us, user’s encrypted data is called “the pill”. A capsule of encrypted information that only the user know what it contains. This is how a pill look:
V2h5IGRvIHlvdSBhbHdheXMgd2FudCB0byBrbm93PyBJIHN3ZWFyIHRoYXQgd2UgZW5jcnlwdCB1c2VyJ3MgZGF0YSBtdWNoIGJldHRlciB0aGFuIHRoaXMgbWVzc2FnZSA6KQ0KDQpTaW5jZSB5b3UgYXJlIGhlcmUgSSdsbCB0YWtlIHRoZSBjaGFuY2UgdG8gc2F5IHRoYXQgd2UgaGF2ZSBhIFBhdHJlb24gY2FtcGFpZ24gd2hlcmUgeW91IGNhbiBzdXBwb3J0IFBhc3NQaWxsIHByb2plY3QgYW5kIGdldCBzb21lIGdvb2RpZXMuIFRoYW5rcyENCg0KaHR0cHM6Ly93d3cucGF0cmVvbi5jb20vcGFzc3BpbGxwcm9qZWN0
Do you know what’s inside? I don’t.
Our mission is not to know, but just to store it for the users and give it back to them when they request it. From now we will represent “the pill” in a simpler way:
The problem is: how do we know what pill belongs to what user? Looks like we need at least an username
(speaking in authentication terms, otherwise it can be better called the pillname
) to associate to the pill. We don’t want to give the pill to anyone that claims it either, we will need a password
too to make sure that the user requesting has the rights of getting it.
Usernames and passwords make us look like we are starting to store personal information, like if we were not trustable anymore. There are 2 facts that will convince our users that they are using our tool anonymously:
- The user will not send their plain username and password, but hashes of them. For the server is impossible to decrypt those hashes to get the real credentials.
- Those hashes are not used to encrypt or decrypt the pill, so even if they are intercepted, the pill content will still be safe.
Storing the pill
The server will then receive a usernameHash
, a passwordHash
, and a pill
that is already encrypted.
The steps for storing the pill are the following:
- Concatenate the
usernameHash
andpasswordHash
in a single string and hash them together to create theuserAuth
token.
- Concatenate the
userAuth
and thepill
to get theauthPill
.
- Store the
authPill
using theusernameHash
as the key.
Where to store the authPill
s is up to you. You can use a key/value storage system, a relational database table or even a file, using the usernameHash
as the file name.
As part of the PassPill Project we will be writing articles about different strategies for storing the data, using most popular cloud infrastructure platforms.
Retrieving the pill
Things are doing well, we have a good bunch of pills stored in our backend. But how can their owners get them back? To get a pill back from the server, the user needs to provide the usernameHash
and the passwordHash
again.
Thes server then will follow these steps:
- Use the
usernameHash
to get theauthPill
. - Exactly as the step 1 of the storing process (see figure 2.1), concatenate the
usernameHash
andpasswordHash
in a single string and hash them together to create theprovidedUserAuth
token.
- Compare if the
providedUserAuth
matches the beginning of theauthPill
.
- If the tokens matched, strip the
userAuth
from theauthPill
getting the rawpill
, and return it to the user.
That’s all it takes to create our ignorant server. I hope you could follow the article so far because it gets far more complicated in the front-end. Let’s have a look at it :)
Creating the front-end
So far we have designed a storage system. By itself is not private nor secure, it receives some credentials and store or retrieve data using them, but if the credentials are not hashed or the data is not encrypted we can perfectly know what and whose is the information.
Let’s design then the most important part to make our system private and secure: the front-end. When we say front-end we mean a web site, a mobile app or whatever way users can introduce their credentials to get his data.
As an example, imagine that our username is myName
, our password is myPassword
and our data is mySecret
. Not really secure credentials, don’t you think?
Our system will make them stronger, but remember that using a password like that is not the best idea if you want to keep your data safe.
Knowing our tools
To encrypt and decrypt the data we are going to use the AES256 algorithm. AES256 takes an encryption key, like our password, and uses it to create an encrypted version of our data. The same key is used to recover the plain data again. We can see it like password protecting a file of our computer, in fact, it’s the algorithm used by Apple’s filevault or Android’s filesystem encryption.
AES256 is secure in the sense that there is no way of knowing the content of an encrypted text if we don’t know the key, but it is really fast encrypting and decrypting data and an attacker can try a lot of keys per second trying to discover ours. This fact makes the AES algorithm as secure as our key, since our password is easily predictable we are not in a good position.
Fortunately, our frontend won’t be using our plain password as the encryption key, we will make things hard for the attackers.
By hashing our password with an algorithm called bcrypt will be creating a secure key. Differently to other hashing algorithms, bcrypt is designed to be slow, it’s really expensive in terms of computational power, specially memory. We can make it as slow as we want and that’s good because if an attacker want to try many passwords, it will need to hash everyone of them beforehand.
Imagine that hashing our password into the encryption key takes 1 second, that’s asumible for our users, we can show a cool “decrypting…” animation to them in the meanwhile. For attackers is horrible, only 1 key can be tested every second if they want to try most common passwords.
Encrypting the data
Bcrypt takes our password and, using a random string called salt
, applies some mathematical magic to hash it, to convert it in something different that will be our encryption key
. It can iterate and apply the same mathematical operations once and again making is execution as slow (secure) as needed.
If every user have a different salt
, attackers can’t create a dictionary of common password hashes that can work for everyone.
This way, a big part of the strength of our system resides in the process of generating the encryption key
. That makes our myPassword
not to look that bad. Anyway, it’s one of the worst passwords you can have, don’t use it.
The front-end app follows the steps below to create the pill:
- If it’s the first time we encrypt the data we need to generate a random
salt
.
- Use bcrypt with the user’s
salt
and thepassword
to generate the encryptionkey
.
- Use AES with the key to encrypt the data, generating the
encryptedData
.
- Concatenate the
salt
and theencryptedData
to generate the pill.
Finally, we have completed the recipe to cook our pill, the ingredients were just your password
, some salt
and the data
we wanted to store.
The salt
is stored in the pill, so we will need to actually fetch the pill from the server in order to generate the key
that decrypt it.
In the server side we know the salt
, but we don’t know the password
so we can’t access to the information either.
Sending and fetching the pill
The pill can be sent to the server now. Imagine that somebody intercepts the communication at that moment. We should always use secure SSL connections for the communication between the back and the front-end but, for demo purposes, imagine that some hacker intercept our plain communication.
They will see our userNameHash
, passwordHash
and the pill. If they can decrypt the passwordHash,
they will be able to access to the data, so we need a secure hash that can’t be easily broken.
Using algorithms like MD5 or SHA1 for the password hashing is like send them in plain format. They are vulnerable to attacks and there are some websites that contains dictionaries to decrypt billions of hashes in the blink of an eye. This one for example.
As we have seen before, bcrypt is a better option to securely create a hash our password
. It requires a salt
that we will generated from our userName
and password
.
That slice
operation after the hashing is because we just need 22 characters for the salt. A prefix that define options for the bcrypt algorithm is needed to complete the salt, we won’t cover it here but you have a great explanation in this stack overflow response.
Bcrypt use to store the salt along with the hash, but since our salt is predictable and we never try to rerun the hashing in the server side, we don’t need it and we will strip it from our passwordHash:
The last piece for our users’ authentication is the usernameHash
. It’s not the critical part of our security, so a SHA256 hashing will be enough:
SHA256 returns a string with a hexadecimal number of 64 characters. If we feel that it’s too long for being used as the key for storing the pill, we can convert it to base32 or base64 to make it shorter. We can even use a part of it, we will have a slightly bigger chance of 2 usernames having the same hash, but it’s safe if our server responds that the pill already exist when trying to create the second one.
Decrypting the data
So far, we’ve been able to encrypt the data and send it to the server with our hashed credentials. The server has stored our data and using our usernameHash
and passwordHash
we can get it back.
The server send us the pill and we need to decrypt it. Do you remember the figure 4.4? The pill is compound by the salt
and the encryptedData
, with the password
provided by the user we have everything that we need to reverse the encryption process:
As you can see, AES256 is a symmetric key algorithm, using the same key with the encrypted data returns the original data again.
Most cryptography articles are written for experts but I wanted this one to be understood by any programmer. Some steps are explained in a very simplistic way but I hope you got the point and take it as a starting point for understanding this kind of systems.
This article is part of the PassPill Project that wants to share the whole development of a web app. In fact, it describes how PassPill encrypts the information. I am not a cryptography expert, so any advices on how to improve the encryption system is appreciated!
If you liked the post, don’t forget to follow our publication and @passpillio in twitter in order not to miss any update!