This is part 3 of a multipart series on how to secure your API in golang. This post will talk about dealing with passwords, tokens and generally secrets. We won’t go into the cryptographic details here and instead focus on the best practices because I want this to be a short and concise guide rather than another blog post about hashes and rainbow tables.
Hash your passwords
This post is not going into the details of why you should hash your passwords, but you need to look for mostly two things:
- When dealing with user-specified data, always use cryptographic salt.
- Use a hashing algorithm designed for passwords, specifically
Let’s start with bullet point number one and let’s focus on passwords even though it applies to all user-specified (or user-generated) secrets. Let’s say someone uses the password
password12. Since hashing the same data will always yield the same result the hash of
password12 will always be the same, e.g. with SHA2:
>$ sha256sum <(echo -n password12) b3d17ebbe4f2b75d27b6309cfaae1487b667301a73951e7d523a039cd2dfe110
b3d17eb for short. Now, a smart hacker will precompute all well known passwords or even precompute just a huge amount of hashse in rainbow tables. If they find the hash
b3d17eb in their data, they know the user used
password12. We want to prevent that, so instead of hashing the password, we generate some random data and hash it together with the password. The random data is called
salt and needs be read from a cryptographically secure RNG. We want to use a unique salt for each password, so in go we would generate a new salt like this:
If our salt is for example
fj39e43, we would hash the following data:
>$ sha256sum <(echo -n fj39e43password12) a2aa997ea3af0f7a1aacd4d67098a727d7e42427186aa5e9ac5591bc19e4f4f1
As you can see, the hash is now different and no one will know
password12 was used as a secret. We store the hash together with the salt in the database and that’s it. The second point is emphasizes more which algorithm to use. We used SHA2 as an example, but it is a bad hashing algorithm for passwords: It’s too fast. Ideally, you want an algorithm that’s slow, but not too slow for your servers. To make it short: The algorithm you are looking for is called argon2 and since we have an implementation, there is no need to use a different one. In go, it is very easy to use.
As you can see, we pass a variety of parameters into the function. There is also a different funciton called
Key which uses the argon2 varian
argon2i while the function
argon2id. The draft recommends using
argon2id if unsure. The parameters you see above are also recommended parameters, but you should check the docs, the reference or the RFC.
Hash your secrets
I want to make the point that you should probably hash everything that is a secret. That also includes secret data generated by you, e.g. session keys. However, for this type of data, since it is (hopefully) generated with cryptographically secure RNGs and of sufficient length, SHA2 will suffice as a hashing algorithm and you don’t have to use argon2.
Some people say, session keys or one time use tokens don’t need to be hashed. It’s true that the risk is much lower and probably the damage an attack can deal is also lower, but since hashing is so cheap, you should do it anyways.
Don’t expose your tokens
Something I see often in REST APIs is that people use secret data in their GET requests. Suppose we have a route to lookup some type of tokens for our admin panel and the route would look like this:
GET /tokens/:secret. Since it adheres to the RESTful principles to use a GET for a retrieval, lots of people use it like this.
However, this might leak your secrets through browsing history or other means since the route GET takes is not really considered something secret. Instead, you should think of it differently: You are not requesting a resource, you are requesting an action, namely the lookup of a resource. And your action will be triggered by post:
POST /tokens/lookup. This allows you to put the secret inside the request body and not leak it somewhere else.
Continue with Part 4: CSRF.