Recently, I got a chance to setup a authentication token mechanism on our middleware, which purely exposes REST API’s. The username/password combination were stored in the Database with passwords encrypted/one way hashed using a salt(secret key).
Quoting Bruce Schneier and Wikipedia:
“A cryptographic hash function is considered practically impossible to invert, that is, to recreate the input data from its hash value alone. These one-way hash functions have been called ‘the workhorses of modern cryptography’.”
The initial authentication API call from the client would need username and password to be supplied. The password supplied should be in Base64 encoded form(though it’s just Base64 encoding and it could be decoded easily, but it would certainly avoid sending clear-text password). We used HTTPS, so this still ensured our body content was encrypted and secure. This password would be decoded on our middleware, one way hashed using the salt and compared with the one in DB along with username.
The password was one-way hashed by using a salt, and since we were on Java+Spring, we used MessageDigest to one-way hash our password+salt, with MD5 algorithm. MessageDigest is available as a part of JAVA security package. Finally, the digested message was converted to Base 16(Hex).
If the combination matches with the credentials in DB, we create a token, formed by the combination of username+timestamp, encrypt it using a secret key and send it back to the client in the form of a JSON web token(JWT). Else, a status code of 401, which is HTTP Unauthorized is returned with an appropriate message.
“JSON Web Tokens are an open, industry standard RFC 7519 method for representing claims securely between two parties.”
This token would be supplied in every subsequent API call in the “Authorization” header by the client. The token received on the middleware, would be decrypted, and the username retrieved from the token using the same secret key used to encrypt the token. If we are unable to decrypt the token, it means the token is invalid. Also, the timestamp retrieved from the token would be checked against for validity of 90 minutes. If it is more than 90 minutes, then a 401, HTTP Unauthorized status with an invalid token message would be sent to the client.
Storage of salt and secret key stirred a lot of thoughts in our mind. There were many ways to do it like storing them in the DB, loading them dynamically from a file, putting them in a configuration file within the app, and lot others. We chose to put both the salt and secret key on the servers with restricted file permissions, loaded them dynamically into Sprint Context and they were not be printed anywhere in the logs to ensure security.