Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to secure a small php api with public and private key

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.

  1. public key is stored in the application to let the user or application login.
  2. server creates private key for this particular user when it's accessed. Or should this be always the same or a static value that has been created by a person?
  3. user makes request sends along with the request a signature that's hash_hmac(some values+private key);
  4. server checks if these value's are correct and does by creating the same hash from the value's that are send.
  5. If server generates the same hash, the request is valid and can then be executed.
  6. Is this they way to go or am I missing some mayor things here.

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

like image 572
Iason Avatar asked Oct 26 '15 14:10

Iason


People also ask

Which is the most secure way to use an API key?

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).

How do I securely send an API key?

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.

Is API key public or private?

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.


1 Answers

Secure Remote Password Protocol (SRP6a) With HMAC Fits Your Requirement

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.

How It Works

The registration flow would be:

  1. Client API registration form generates a random API password using JavaScript at the client which is not transmitted to the server. This is saved into the browser local storage and shown to the user asking them to print it off and keep a backup.
  2. The password is given to the Thinbus SRP client JS library code which outputs a client salt and password verifier.
  3. The salt and verifier are posted to the server and saved in the database for that client. Normally Thinbus recommends you keep the verifier hidden by using HTTPS to send the verifier to the server to prevent brute force attacks to recover the password. If you are using a random generated password as long as a typical software license key then you can transmit the verifier over HTTP. See footnote 2 below.

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:

  1. Client javascript loads the API password from browser local storage.
  2. Client AJAX fetches from the server the client salt and a server random one-time number B.
  3. Client javascript generates a one-time number 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.
  4. Server uses the password verifier saved to the database at registration, the client salt, and the two random numbers to compute the session key K then confirms the client sent password proof M is good. If that is all good it sends its own proof M2back to the client. At this point the client has been authenticated using STP as a zero-knowledge proof of password.
  5. Client checks 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.
  6. All API requests and responses can be HMAC signed with the shared secret and verified on the other side.

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.

like image 191
simbo1905 Avatar answered Sep 20 '22 14:09

simbo1905