I'm trying to design a small api however I'm a bit stuck on how to secure the api. I have read some articles about how to do this one of them is: Login and retrieving an apikey and then hash some values with this apikey and send the hashed string back along with the request, so it can be done again on server level.
Is this a good way or is this dangerous to do?
If not miss understood, to avoid man in the middle I can add the request url to the variables that will be hashed, or isn't that the appropriate way
Also my brain is stuck on how to use a time stamp to avoid making lots of request to the same url with same data.
I'm sorry if my question have been asked a 1000 times. However I have read some articles now and it's still not clear to me what way to go for my small api.
From what I have read and understand from it this should be the way.
For hasing the data is the underneath way a good way to create a hash?
$l_sPrivateKey = 'something returned by database when user loged in';
$l_aData = array();
foreach($_POST as $key => $value){
if($key == 'signature') continue;
$l_aData[$key] = $value;
}
//This should then be the same as $_POST['signature'];
hash_hmac('sha256',serialize($l_aData),$l_sPrivateKey, false);
Your input would be appreciated. Kind regards and thanks in advance
One way to improve security is to keep the API key out of the channel. Instead of adding the plaintext API key to a request, we will use the API key to sign each request. This is typically done using a hash-based message authentication code (HMAC).
Before sharing your API key, regenerate it and label it as the newest shared key. Don't share API keys through email. Always use HTTPS/SSL for your API requests — some APIs won't field your request if you're not using it. Assign a unique API key to each of your projects and label them accordingly.
An API key simply identifies you. If there is a public/private distinction, then the public key is one that you can distribute to others, to allow them to get some subset of information about you from the api. The private key is for your use only, and provides access to all of your data.
The following assumes that your API is browser-to-server so JavaScript-to-PHP not server-to-server using only PHP. SRP will work for both scenarios but the answer below discusses browser-to-server libraries.
Use the Secure Remote Password protocol to authenticate the user of the API which has the side effect of creating a strong session key. You can then use the shared strong session key to sign API requests and responses using HMAC.
RFC5054 uses SRP rather than public keys to create a shared session key to encrypt TLS traffic. There is an implementation in OpenSSL. This demonstrates that SRP authentication is a perfectly safe replacement to public keys to create a secure shared secret. IMHO using SRP is more convenient to solve your problem.
The Thinbus SRP library is a JavaScript SRP library which has a demo of authenticated to a PHP server. The PHP demo does not show using the shared session key but it is simply $srp->getSessionKey()
on the server and client.getSessionKey()
in the browser once the authentication protocol has finished. The default Thinbus configuration results in a 256bit shared key. You can use this with HMAC see the footnote 1 below about using signed JSON.
The registration flow would be:
The API usage flow would start with an SRP authentication of the client that has the side effect of generating a session key. Note all this is in the Thinbus demo code as "standard usage" but is explained here to give a flavour of how STP authentication works. This authentication protocol is shown in sequence diagram of the thinbus page and is running in the online demos:
B
.A
then uses the password, salt, and both one-time numbers to generate a session key K
and hashes that with the both one-time numbers to create a password proof M
that it posts to the server along with its random A
.K
then confirms the client sent password proof M
is good. If that is all good it sends its own proof M2
back to the client. At this point the client has been authenticated using STP as a zero-knowledge proof of password.M2
against its computation. If all is good both sides have a shared secret K
which is a one time 256 bit session key derived from the random A
and B
that no man-in-the-middle can feasibly know.All of the above is covered in the PHP demo of Thinbus minus actually calling $srp->getSessionKey()
at the end to have a key that can be used to sign things with using HMAC.
Give that SRP replaces password authentication with a cryptographic zero-knowledge proof of password it is surprising that not all developers use it by default. The fact that it also generates a shared session key for API signing is simply an added bonus.
Footnote 1: Most APIs would prefer to post one JSON value with all the data in it. This is because JSON is simple yet more powerful with built in API in both PHP and JavaScript to turn objects into strings and back again. As @dbrumann pointed in a comment there is a standard for signing JSON which is JWT. Google suggest that here are libraries for this in both PHP and JavaScript. So if you upgrade to passing one JSON input value and returning one JSON output for every command in your API Ayou can use a JWT library to sign and validate the JSON inputs and outputs of the API. One of the JWS algorithms is "JWSAlgorithm.HS256 - HMAC with SHA-256, 256+ bit secret". The libraries will sort out the mechanics of actually signing and verifying so you don't have to write that code and worry about possible security bugs.
Footnote 2: The recommendation with Thinbus is to transmit the password verifier to the server over HTTPS to keep the verifier secret. This is to prevent interception then an offline dictionary attack against the password verifier to recover the password (i.e. the password is salted into the verifier so you would need to run the 16G crackstation password dictionary through the verifier generation code with the user salt to find a match). With API usage the browser window.crypto API can generate a truly random "API key". Typically windows keys were 16 upper case letters shown to the user formatted as XXXX-XXXX-XXXX-XXXX. Checking the GRC password search space page it says that a random 16 letter upper case password that size would take a government 14 years to exhaustively search. Given that estimation you can safely transmit a password verifier generated for such a long random password over plain HTTP without encryption as no-one will feasibly dedicate many years of computing power to run so many password guesses through the verifier generation algorithm (which uses the random client salt so cannot be pre-computed) to find a match to recover the client API password.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With