3

I'm building a restful API for a php application. At the moment, the API will only accept and respond with json. The request, routing and response is all handled by the framework, but I needed to build a custom authentication mechanism.

There are two items that I wanted to add in for extra security and to avoid replay attacks: a timestamp and a nonce.

  1. Besides these two items, I wanted a sanity check to ensure that I have not missed anything else really obvious from a security or usability point of view.
  2. Should the entity_id go in the header instead of the request?

This is what I have for authentication so far:

function authenticate_request()
{
    $request = json_decode(file_get_contents('php://input'));
    $request_headers = apache_request_headers();

    if ( ! isset($request_headers['X-Auth']) OR ! isset($request_headers['X-Auth-Hash'])) {
        return false;
    }

    $user = User::get_by('public_key', $request_headers['X-Auth']);

    if ( ! $user) {
        return false;
    }

    // every request must contain a valid entity
    if (isset($request->entity_id) && $request->entity_id > 0) {
        $this->entity_id = $request->entity_id;
    } else {
        return false;
    }

    $entity = Entity::find($this->entity_id);
    if ( ! $entity) {
        return false;
    }

    // validate the hash
    $hash = hash_hmac('sha256', $request, $user->private_key);

    if ($hash !== $request_headers['X-Auth-Hash']) {
        return false;
    }

    return true;
}

Example curl request:

$public_key = '123';
$private_key = 'abc';

$data = json_encode(array('entity_id' => '3087', 'date_end' => '2012-05-28'));
$hash = hash_hmac('sha256', $data, $private_key);
$headers = array(
    'X-Auth: '. $public_key,
    'X-Auth-Hash: '. $hash
);
$ch = curl_init('http://localhost/myapp/api/reports/');

curl_setopt($ch,CURLOPT_HTTPHEADER,$headers);
curl_setopt($ch,CURLOPT_POSTFIELDS, $data);
curl_setopt($ch,CURLOPT_RETURNTRANSFER,true);

$result = curl_exec($ch);
curl_close($ch);

print_r($result);

1 Answer 1

2

hash_hmac() expects its second parameter to be a string, you're passing your decoded JSON object instead. Other than that, your approach seems pretty standard. entity_id should also be protected by the HMAC signature, so I'd keep it in the request body or your signature calculation will get a little bit more complicated for no real gain.

Sign up to request clarification or add additional context in comments.

2 Comments

I'm passing json_encode to the hash_hmac, not json_decode, so I think thats ok. Does using this approach mean that there cannot be any GET requests to the API - since there always has to be CURL_POSTFIELDS?
That json_encode you mention is not present in your posted code. With the code you have posted it is obvious that you cannot have GET requests to your API, since you require an entity_id key in a JSON POST body, which will never be present in a GET request. You will need to have a very different signature algorithm for GET requests, unless you decide to pass an entire JSON string as a single GET parameter.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.