Reputation: 41
I am currently building a RESTful API in PHP which will be used to power both a web and mobile application. As such, the API should be considered public. However, I want to authenticate users when they make requests to the API from either mobile or web.
When a user registers for the service, I generate a unique API key and secret which is stored against the user's record in the database. Ideally, I want requests to be made only using the API key using JavaScript or PHP.
I thought about establishing a hand-shake system for authentication, with the process as follows:
I was thinking to use HMAC to generate the token, something like this:
$token = hash_hmac('sha256', $user->apiKey.microtime(), $user->apiSecret);
The $token
will then be stored in the DB, and should be unique for every request. In turn the requests can then be made using the following jQuery code:
$.getJSON('/api/user/get/1', { 'key': '123rrwfnufsd7f72' }).done(function(data) {
// data will now contain a token, so we use it to make another AJAX request:
if( data.token )
{
$.getJSON('/api/user/get/1', { 'token': data.token }).done(function(user) {
// do something with user or handle bad token
});
}
});
My question is will this be sufficient to prevent brute-force and replay attacks?
Upvotes: 4
Views: 11278
Reputation: 119
I just crreated a small lib about this. You can specify a time drift in minutes if need, or even use a custom salt. ( see https://github.com/gboddin/psk-validator/ )
This library allows you to authenticate signed message from a client using time based salts.
composer require gboddin/psk-validator
$sharedsecret = '43223ff65b6ce17072cda5729b20daceec611d1f39e76040d347ceeca51d2a47';
$data = json_encode(['suff','otherstuff',['machin' => 'bidule']]);
/**
* Client :
* Invoke the validator with the pre-shared key and an algo (sha256 by default) and
* define an allowed time drift in minutes ( 2 by default ).
*/
$sigValidation = new \Gbo\PSKValidator($sharedsecret, 'sha256');
/**
* Signs a bunch of data and get the signature.
* The second optional parameters allows for a user provided salt instead
* of the default time based salt. It must be agreed on between client and server.
*/
$signature = $sigValidation->sign($data, null);
/**
* Server :
* The optional third parameter allows to define a maximum time drift in minutes ( default 2 minutes )
*/
$signature = $httpRequest->getHeader('x-signature');
$sharedsecret = '43223ff65b6ce17072cda5729b20daceec611d1f39e76040d347ceeca51d2a47';
$sigValidation = new \PSKValidator($sharedsecret, 'sha256', 2);
$data = $httpRequest->getBody();
/**
* Server :
* The third optional parameters allows for a user provided salt instead
* of the default time based salt. It must be agreed on between client and server.
*/
$signatureIsValid = $sigValidation->verify($data, $signature, null);
var_dump(
$data,
$signature,
$sigValidation->getTimeBasedSignatures($data),
$signatureIsValid
);
string(41) "["suff","otherstuff",{"machin":"bidule"}]"
string(64) "d85a2d6873e034cb3ab8c490cb82139d8dabae6c08581cca0a2e7497ead287a4"
array(5) {
[0]=>
string(64) "d85a2d6873e034cb3ab8c490cb82139d8dabae6c08581cca0a2e7497ead287a4"
[1]=>
string(64) "dc150239c61fe272b7ca44ad0918d159a84e5bc1661db48bad04a81bc7f4c742"
[2]=>
string(64) "e1822fc6cc7bbf1184b29efaaaceac6d598fb406b4f8cf9b3717b3d0c533c19f"
[3]=>
string(64) "d85a2d6873e034cb3ab8c490cb82139d8dabae6c08581cca0a2e7497ead287a4"
[4]=>
string(64) "d85a2d6873e034cb3ab8c490cb82139d8dabae6c08581cca0a2e7497ead287a4"
}
bool(true)
( sorry for the self promotion :) )
Upvotes: 4