I am playing around with authentication methods in my RESTful API project and I really like the idea of generating HMAC-SHA256 signatures as an authentication method.
The client is creating the signature with few simple steps:
# example client-side code
sig = hmac.new(bytes('SUPER_SECRET_KEY', 'utf-8'), b'', sha256)
sig.update(request_path)
sig.update(request_body)
# ...other variables needed for generating the signature...
signature = sig.hexdigest()
and adding it to request header along with his "user name" (e.g. Authorization: THE_USER_NAME:abcd1234xyz890
).
On server-side, I am trying to re-create it in the same way:
# example server-side code
def do_check(request):
# get user name from request header
username = request.headers['Authorization'].split(':')[0]
# some method to retrieve the "secret key" from database
user = db.User().filter(username=username).one()
# use user's "secret key" to generate the signature
sig = hmac.new(bytes(user.key, 'utf8'), b'', sha256)
sig.update(bytes(request.path, 'utf-8'))
sig.update(request.data)
# ...other variables needed for generating the signature...
return sig.hexdigest()
# compare the returned signature with the one client sent us...
All this works fine as long as I store the user's key as a plain text in my database:
| username | key |
------------------------------------
| THE_USER_NAME | SUPER_SECRET_KEY |
We all are aware that this is absolutely unacceptable, so I tried to simply hash the SUPER_SECRET_KEY
with bcrypt
and storing a hashed string instead:
| username | key |
--------------------------------------------------------------------------------
| THE_USER_NAME | $2b$12$UOIKEBFBedbcYFhbXBclJOZIEgSGaFmeZYhQtaE4l6WobFW1qvIf6 |
The problem I am facing now is that client used un-hashed version of the "secret key" to generate the signature which I am unable to do on server-side since I don't have it in plain-text anymore.
One of the examples of a similar approach is generating HMAC signature in Amazon Web Services (also simplified explanation of the same process) which does not require any additional log-ins or authentication, nor does provide any tokens or "replacements" for the key/secret combination. I really doubt that AWS is storing the secret in a plain text in database(?)
How can I recreate the HMAC signature on server-side with hashed version of "secret key" in database, while not forcing the client-side to change its method of signature generating (e.g. avoid installing bcrypt
or even hashing the secret at all)?