How to never store or even know your user’s passwords? — Implementing secure authentication with AWS Cognito and SRP ( Part I )
How do you store your users’ passwords?
I hope it’s not in plain text. Like Facebook did once.
You are probably salting the user’s password, hashing it, and storing the hashed value + salt somewhere safe. So even if an attacker hijacked your password store, it’s relatively hard for them to do a brute-force attack or use a precomputed dictionary of hashes ( a rainbow table ) to get the actual passwords of the users. This is pretty secure considering that you’ve used a computationally expensive hashing algorithm and were cautious not to persist the password in an unencrypted logs store, also added some password strength measures, so your users can’t use ‘dadada’ as their password like Mr Zuckerberg.
But is this the most secure way to manage the passwords of your hard-earned users? Well, there have been numerous password leaks of well-known brands with millions of users in recent history proving it isn’t.
Zero Knowledge comes to save
So what’s a more secure way? Well, security issues here arise mainly due to the server needing to access the user’s password at login. The server needs to check that user sent password is valid by hashing it and comparing it with the previously stored hash at user registration. So you need to make sure you’re handling the user-sent plain text password securely. Also, the communication between client and server should be secure as well. MITM ( man in the middle ) attacks could happen even if the server has a TLS certificate.
But does the server REALLY need to know the user’s password for authenticating? Isn’t it enough, if the server can somehow verify that the user knows the password, without explicitly asking what is his password?
Hmm, It should be enough. But can this be done? It’s like you want to prove to a bouncer at the club, that you’re not underage without showing your ID.
Well, that is possible thanks to the zero-knowledge proofs. It is an absolutely interesting cryptographic protocol in “which one party (the prover) can prove to another party (the verifier) that a given statement is true while avoiding conveying to the verifier any information beyond the mere fact of the statement’s truth”.
If you’re someone interested in the blockchain space, you’ve probably heard about this before. This has been a really hot topic with all the scaling solutions for Ethereum. However, if this is the first time you’re hearing about zero-knowledge proofs, I know this could sound completely unintuitive. So I suggest you take a look at this video to intuitively understand how zero-knowledge proofs are possible and also go beyond that.
SRP
Ok, the next question you might have is, how am I gonna incorporate this zero knowledge thing into the authentication workflow? And, that’s where SRP comes in. SRP is the acronym for Secure Remote Password Protocol. Which is a cryptographic protocol for authenticating users without ever needing to store or transmit passwords between server and client. Instead, the client sends a zero-knowledge password proof to the server, which proves that the user knows the password without revealing anything other than the fact that the user knows the password. Sounds fascinating. Right?
If you want to see how to use this in practice, I recommend this article. This depicts how to use SRP for authentication, with the help of thinbus-srp-js
library. Which is an implementation of the SRP protocol in JavaScript. When incorporating SRP, it is encouraged to use tested and proven open-source libraries that are available for your language.
How SRP Works
If you’re not from a strong cryptography background, understanding these concepts might be tricky. There are not many resources available that distill these concepts. So you have to go directly to the protocol design documents and archived internet forums looking for answers.
there are ways you can get the benefits of SRP, via completely managed authentication solution providers, which we’ll see in a second. If you’re using a managed solution like that, you won’t need to know all these behind the scenes details of SRP.
Anyway, if you’re planning to implement SRP using a library like thinbus-srp-js
it’s important to understand the fundamentals at a high level. Here’s what happens, in a nutshell. In SRP password never leaves the client device. Instead, a verifier is computed based on the username/email, password, a random salt, and some constants. Then when a user wants to log in, he creates a cryptographic proof that he knows the password. then the server verifies that proof using the previously created verifier + some other values.
That’s an oversimplification of the protocol. Below you’ll find a bit more details that could help you understand the protocol. I wouldn’t say it’s a deep dive though. Anyway, let’s have a look.
User Registration
When a user registers for the platform, they will first type username/email and password in a standard registration form as usual. Then a random salt is generated and it gets concatenated with the password and username (username is optional) to generate a private key ( this is the x
value in SRP design spec ). Then this private key is used to derive a value called “verifier”. As you can see above thinbus
library abstracts most of these details by giving us generateRandomSalt
and generateVerifier
functions. Then this verifier, salt alongside an email/username gets sent to the server. Remember, even if an attacker gains access to the verifier, he can’t use that to impersonate the user. Why? Because the verifier is not the password. It’s just a value that will be used later in an algorithm, to verify that a user-sent cryptographic proof is valid.
User Login
Ok, now the registration is done. Here’s what happens at login. Below is a screenshot of variable names that will be referenced during the rest of this article. This is from the design page of the SRP protocol.
* side note:- the SRP protocol was the outcome of a project started in 1997 at Stanford University. SRP6a is the latest version of the protocol, which has been widely tested and widely deployed.
Let’s use the diagram in thinbus-srp-js
package docs, to get a better visualization of the process.
The user first sends a request to the server, to get the salt. When responding to this request server generates and sends back a public ephemeral value ( B
), alongside the salt. Now to complete the authentication, the client also generates a public ephemeral value ( A
), and compute a password proof ( M1
). Client uses the public ephemeral value from the server ( B
) + private values generated on the client side when computing this proof. Then the server uses the private + public information available to it ( the verifier, private ephemeral value b
, client sent public ephemeral value A
, etc. ) to verify the password proof M1
.
And voila, authentication is done!
The server can confirm that this user knows his password, without seeing his password.
So as I said this is not a deep dive and by no means I’m a cryptography expert. But I hope this gave you a fair understanding of the protocol, and why it’s beneficial. If you want to further solidify your understanding, I suggest you take a look at this simplified implementation written in Python found on the Wikipedia page. Also, check out the design page and official documentation of the SRP protocol and thinbus-srp-js
docs.
SRP with AWS Cognito
Ok, now at this point you might be a bit overwhelmed and probably think this is a lot of work for password authentication. Even though you think this is a better solution that addresses current shortcomings in auth flows, you might hesitate to implement all these.
But don’t worry. There’s a far easier way to get all the benefits of SRP without even realizing you’re implementing SRP. All you have to do is just use AWS Cognito to authenticate users in your application.
AWS Cognito provides a fully managed backend for all your authentication needs. By default, Cognito user pools, enable SRP authentication in the backend. For the client-side implementation of SRP, you can use AWS amplify, AWS SDK directly, or Cognito hosted UI.
Using Cognito Hosted UI
I’ll give you a brief intro to these options. Hosted UI is the easiest one. This is basically a hosted frontend that you can enable when you’re creating the Cognito user pool. AWS manages the API calls to Cognito backend, hosting the frontend, scaling, and all the stuff. And of course, you can set up a custom domain. You can customize the styling by simply adding a CSS file. Ok, so the pro with this option is it’s really easy to set up. The con is you only have limited control over the appearance and UX of your application.
Using AWS Amplify
Ok, the next option is Amplify. So I’ll give you an overview of using Amplify. But not in this article. This already took me longer than I anticipated to write. I’m gonna break this into two parts. The second part will include how to create a Cognito user pool, ( I’m gonna use AWS CDK for our Infrastructure as code solution ), then how to use Amplify for client-side implementation in a react app. We’ll also delve into the network requests to examine how SRP works in practice with AWS Cognito.
Great, you made it to the end. Thank you for reading. Hope you’ve gained some value out of this. Don’t forget to contribute to our shared knowledge below in the comment section. Also here’s my socials if you’ve any questions, wanna get in touch, or just see what I’m sharing is of value to you.
https://www.linkedin.com/in/akalanka-pathirage
https://twitter.com/AkalankaPathi
See you in part two. Adios!