I have a service where users each have an API key. I need to store the keys so that they can be used to validate API requests.
If I store the keys in plaintext in my database, I'm worried about the scenario of someone getting access to the db, grabbing all the plaintext api keys, then using them to impersonate others (there will likely be bigger problems if someone got access to the db, though).
This is similar to storing user passwords, where you just store the hash and validate using that - however most APIs let you view your API keys, which means they need to be stored in some recoverable way.
Is there a best practice for this?
The threat that someone gets the database and gets the keys means they can use the api keys to access the data in the database, which they already have, so no win there.
The threat that someone can access the database, get the passwords, means they can reuse those passwords on other web sites with the same user name because people tend to reuse their passwords.
Another reason having passwords in the clear or easily reversable is someone in your company could get a hold of the passwords, and start to do bad stuff acting as the user. Which IS a risk you might have if your API keys are in the clear.
Typically, HMAC is a solution for cryptographically computing a secure value from a single secret key, and some public value.
Have a look at HMAC. With HMAC, you can load a secret key into memory with the app (config file, read off of amazon KMS, typed in on app start, or however you want to get that secret key there).
In the database, store a token. Token = UUID() for example. The token should be unique to the user, the token could be versioned in case you need to regenerate, and the token could be random (like UUID). The token is not secret.
The API key is computed using the secret key (SK) and user token (UT) as follows:
API_SECRET = HMAC(SK, UT)
Then distribute that UT (More commonly called API_KEY) and API_SECRET to the user, and when the user tries to connect, you compute the API_SECRET:
Get user record from database (you're probably already asking the user to provide their username)
Compute the API_SECRET from the UT in the database:
API_SECRET_DB = HMAC(SK, UT)
Compare the computed API_SECRET_DB to the one provided in the request:
if (API_SECRET_DB == API_SECRET_FROM_REQUEST){
//login user
}
Bottom line, you only protect the Secret Key, and not every single credential.
I did an update to some library written in PHP which made it using an Impersonate Protection Algorithm (IPA). that lead to not saving the Token itself inside a database.
For more info check this https://github.com/vzool/api-hmac-guard
Hope it helps, Thanks
Related
I am building a website that’s gonna have a pay system that works with the mollie API. In particularly the website needs to send users a payment link for their ordered products. To accomplish that mollie needs to authenticate with a api key. So I need to store the api key somewhere safely.
So my idea is to use AES Symmetric Cipher encryption when the admin registers his api key (CMS). With this encryption I need only one key to decrypt and encrypt the api key. I was thinking of using the plain text password of the admin as the key, because I don’t store this value (I hash the passwords) so it’s only available when then admin types his password. So when the admin wants to send a payment to an user the website will ask his password.
So my question is: Is this is a safe way of storing the api key?
Sorry for my bad English, it's not my native language.
First. API secrets and passwords have different lifecycles (key rotation & password change policies), and possibly different complexity requirements.
Second, The admin's plaintext password shouldn't be used for anything other than signing the admin in. Don't put all your eggs in one basket - you want to limit the scope of damage in case a secret gets compromised.
You would be better off just creating a separate secret for API key encryption/decryption, and storing it in some secret management e.g. Vault, AWS secrets, etc.
If you want to avoid storing the API key altogether, and you're fine with the admin just remembering it, then you can have the admin manually enter the secret, like a second password, but in any case it would be bad practice to couple it with the admin's sign-in password.
In my project there's a requirement to invalidate all jwt tokens of a user when the user changes his password. I was thinking of giving each user a different signing key, and simply reset the key when password is changed. Then I googled around and found Redis is a good place to store those per-user keys. Everything seems to work just fine.
But there one thing I cannot get my head around. Since it has to hit Redis once per request, is it any different than issuing the user an opaque token instead of JWT, and store the token -> JWT payload mapping in Redis?Isn't that defeats the purpose of using JWT?
To invalidate tokens you need to revoke them. OAuth spec also does not require getting secret key from remote server every time you need to validate JWT (as you said it kind of defeats the purpose). The key can be stored locally at resource site.
You have two options here:
1) Introspect the JWT token from resource side against OAuth server every time it validates it. Seems like overkill to me. The best approach is to give short expiration time to JWT token and let the already issued tokens to just expire.
2) Have the resource store the secret key locally and when it fails to validate go and get the key and re-validate it again.
From the point of view of invalidating the token, there's no particular need to store the JWT in Redis - anything that you can check and later invalidate should do the trick.
That said, presumably you're using a JWT for other reasons. For example, it's what the AuthN/Identity service provides. Or perhaps you use it to store claims or other metadata that you validate as part of the AuthN/AuthZ logic. In that case, since it's handy, storing the JWT seems very reasonable.
I am planning to secure my rest API in django with a ACCESS_TOKEN.
When ever user is logged in using their username and password, once they are authenticated, I generate a ACCESS_TOKEN and passed to frontend be it Website or Native application. and then later used that ACCESS_TOKEN for further communication.
I am generating this token based on some user data and then encrypting this with public key. Later when application send this for in any request, I decrypt the ACCESS_TOKEN with private key and extract user data data and process the request. This is something similar to session where session data is in encrypted form in ACCESS_TOKEN and only private key and decrypt the ACCESS_TOKEN. This is what I am planning to do.
Please suggest me for following questions:-
1. Is is the best way to secure my REST API? I want to use my API in same way from Web-application(AJAX calling) and NATIVE application(Android/IOS etc) ?
2. What is the best way to expire the token? Do I need to keep track of access token at my end in order to expire them?
Also I do want to use the Oauth in my API.
Most people I see use JWT's that are signed but not encrypted so they store non-PI data like user_id or session_id. I guess you could encrypt it if needing to store personal information but I don't see any other reason. Assuming you are using HTTPS, then only the end client would have access to the information. Sounds like asking for trouble if the secret gets leaked so you would want a really good key rotation scheme since you may not even know its leaked until too late.
Many people using JWTs do so because they don't want a centralized auth server, thus the token is short lived like a few hours or days. If you need really tight control on expiring tokens, you can take a blacklist approach where a blacklist of JTI's (JWT Ids) are stored in a K/V to be checked against. https://www.moesif.com/blog/technical/restful-apis/Authorization-on-RESTful-APIs/
It's rather straightforward to use the Google Sign-In library on the server side and attain a GoogleIdToken to validate a user's identity. However, I'd like to encrypt per-user data in my database with a secret that's unique to every user. Is there an easy way to do this? If not using Google Sign-in, you can derive keys from a user's password, but that's obviously not possible here.
Well, first of all, you're drawing a parallel to using the user's password to derive an encryption key, but since you're talking about that as an alternative if you weren't using Google Sign-On, that implies your talking about using the password that users would authenticate with. That's a bad idea.
Users need to be able to change their authentication password, and that will be a major hassle for you if you're encrypting with it. It will require you to decrypt everything with the old password and then re-encrypt it with the new one.
So what you need to find is something that you can pull out of the GoogleIdToken that will never change. Email addresses change, so I wouldn't use that. Perhaps the user id, which you can get with GoogleIdToken.getPayload().getSubject() is what you want. Then what you would want to do is derive a key from that. I would look for ways to combine it with other information that the user gives you that really is secret, though.
The information you receive during a Google sign on is intended for authentication purposes. The id token is encoded as a Json Web Token. There is nothing secret in a JWT.
The information is cryptographically signed by the authentication provider, so you can verify the information. This is of no help for deriving secrets, though.
Looks like you'll have to find another way.
There's no way to do this with just Google Sign-In, but you can use Firebase to convert user authentication credentials (with Google or other systems) into storage restricted to access by the user.
You can do this by using Firebase Authentication; you can authenticate your users from your backend, then store the encryption key for the user in User private objects. (Or possibly just store the data you wanted to secure in those objects.)
Then your server can be set up to not have the access rights to read user data unless those users are logged in, although you will still have administrative ability to read all user data.
I came across many APIs that give the user both an API key and a secret. But my question is: what is the difference between both?
In my eyes, one key can be enough. Say I have a key and only I and the server know it. I create a HMAC hash with this key and do an API call. On the server, we create the HMAC hash again and compare it with the sent hash. If it's the same, the call is authenticated.
So why use two keys?
Edit: or is that API key used to lookup the API secret?
You need two separate keys, one that tells them who you are, and the other one that proves you are who you say you are.
The "key" is your user ID, and the "secret" is your password. They just use the "key" and "secret" terms because that's how they've implemented it.
Simple answer, if I understood it correctly...
If you use your API key for encryption, how will the service know who is contacting them? How will they decrypt that message?
You use API key to state who you are, this is what you are sending in plain text.
The SECRET key you do not send to anyone. You simply use it for encryption. Then you send the encrypted message. You do not send the key that was used for encryption, that would defeat the purpose.
One thing that I did not see mentioned here, although it is an extension of Marcus Adams's answer, is that you should not be using a single piece of information to both identify and authenticate a user if there is a possibility of timing attacks, which can use the differences in response times to guess how far a string comparison got.
If you are using a system which uses a "key" to look up the user or credential, that piece of information could be incrementally guessed over time by sending thousands of requests and examining the time that it takes for your database to find (or not find) a record. This is especially true if the "key" is stored in plaintext instead of a one-way hash of the key. You would want to store users's keys in a plaintext or symmetrically-encrypted for if you need to be able to display the key to the user again.
By having a second piece of information, or "secret", you can first look up the user or credential using the "key", which could be vulnerable to a timing attack, then use a timing-safe compare function to check the value of the "secret".
Here is Python's implementation of that function:
https://github.com/python/cpython/blob/cd8295ff758891f21084a6a5ad3403d35dda38f7/Modules/_operator.c#L727
And it is exposed in the hmac lib (and probably others):
https://docs.python.org/3/library/hmac.html#hmac.compare_digest
One thing to note here is that I don't think that this kind of attack will work on values that are hashed or encrypted before lookup, because the values that are being compared change randomly each time a character in the input string changes. I found a good explanation of this here.
Solutions for storing API keys would then be:
Use a separate key and secret, use the key to look up the record, and use a timing-safe compare to check the secret. This allows you to show the user the key and secret to a user again.
Use a separate key and secret, use symmetrical, deterministic encryption on the secret, and do a normal comparison of encrypted secrets. This allows you to show the user the key and secret again, and could save you from having to implement a timing-safe comparison.
Use a separate key and secret, display the secret, hash and store it, then do a normal comparison of the hashed secret. This removes the necessity to use two-way encryption, and has the added benefit of keeping your secret secure if the system is compromised. It has the downside that you cannot show the secret to the user again.
Use a single key, show it to the user once, hash it, then do a normal lookup of the hashed or encrypted key. This uses a single key, but it is not able to be shown to the user again. Has the benefit of keeping keys secure if the system is compromised.
Use a single key, show it to the user once, encrypt it, and do a normal lookup of the encrypted secret. Can be shown to the user again, but at the cost of having keys vulnerable if they system is compromised.
Of these, I think that 3 is the best balance of security and convenience. I have seen this implemented on many websites when getting keys issued.
Also, I invite any actual security experts to critique this answer. I just wanted to get this out there as another discussion point.
There are answers explaining what the secret and (public) key is. It's a public-private key pair that they give confusing names to. But nobody says why the APIs require both, and many APIs only give you one secret! I've also never seen any API's docs explain why they have two keys, so the best I can do is speculate...
It's best to put only your public key in your request and sign the request locally with your private key; sending anything more shouldn't be needed. But some get away with just having the secret in the request. Ok, any good API will use some transport security like TLS (usually over HTTPS). But you're still exposing your private key to the server that way, increasing the risk of them somehow mishandling it (see: GitHub and Twitter's password logging bug recently discovered). And HTTPS is theoretically just as secure, but there are always implementation flaws out there.
But many – actually most it seems – APIs have you send both keys in requests since that's easier than making people do their own signatures; can't have pure cURL examples otherwise! In that case, it's pointless to have them separate. I guess the separate keys are just for in case they change the API later to take advantage of them. Or some have a client library that might do it the more secure way.