Reputation: 4259
I've created a simple RESTful API for a project partially following this very good blog post by Riyad Kalla. Now, I've read dozens of similar questions on Stack Overflow but I can't seem to find an answer to my security question.
Briefly, my requests go like this:
I'm concerned that somebody using my service could fetch the public API key (by sniffing network traffic let's say), and then simply cURL the same request the client would originally do via their browser using AJAX directly to the developer's server. Therefore, the malicious user could be authenticated as a legit user and access the API with somebody else secret API key.
I'll try to give a concrete example. Normally I would do:
But I'm scared that:
Anything I'm missing or is this just part of the RESTful API game?
UPDATE: I am voluntarily omitting any form of timestamp validation to keep things simple and just focus on the authentication issue.
UPDATE 2: I've added a $_SERVER['HTTP_REFERER']
validation to the process. The goal here is that the client must send a referrer along with the request and it must match the referrer listed in the database on the API's side. Unfortunately, HTTP referrers can be faked easily. It is yet another level of security, but still not perfect.
UPDATE 3: I've changed my server side code to set the referrer to the remote IP address. This forces every request sent to my server that wants to be hashed using the secret API key ultimately get to the API server with the original request IP address. This IP can then be validated and the request can go through. I believe it is yet possible to fake $_SERVER['REMOTE_ADDR']
, but it is more complex than faking $_SERVER['HTTP_REFERER']
... Still not perfect I guess.
UPDATE 4: According to these posts: How to fake $_SERVER['REMOTE_ADDR'] variable? and https://serverfault.com/questions/90725/are-ip-addresses-trivial-to-forge, faking the $_SERVER['REMOTE_ADDR']
is possible though difficult. It is, however, impossible to receive a response from the faked request since you're not in control of the faked network. The request can be validated successfully but will its response won't fall unto malicious hands.
Upvotes: 6
Views: 4666
Reputation: 4259
The solution I've found to stop other scripts from using a public API key and sending requests to the server-side HMAC-hashing script is to send the original requester's identity along with the request. I'm using $_SERVER['REMOTE_ADDR']
to determine the original requester's identity since it's harder to fake, and faking it usually means they won't get a response.
/* $this as a class that handles requests */
// Build hash and include timestamp
$this->vars['timestamp'] = time();
$this->vars['hash'] = hash_hmac('sha1', http_build_query($this->vars).$this->vars['token'], API_SECRET);
// Send request to API
curl_setopt_array($this->curl, array(
CURLOPT_RETURNTRANSFER => 1,
CURLOPT_URL => $url,
CURLOPT_POST => $this->method == 'post' ? 1 : NULL,
CURLOPT_POSTFIELDS => $this->method == 'post' ? $this->vars : NULL,
CURLOPT_CONNECTTIMEOUT => 15,
CURLOPT_TIMEOUT => 15,
CURLOPT_REFERER => $_SERVER['REMOTE_ADDR'], // Referer here!
CURLOPT_MAXREDIRS => 3,
CURLOPT_HTTPGET => $this->method == 'get' ? true : false
));
Once sent, the API doesn't only check the secret API key from the database, but it also checks if the $_SERVER['HTTP_REFERER']
is listed as allowed! This also allows the API to accept servers on a per-user basis.
Upvotes: 0
Reputation: 19563
You are on the right track by using HMAC. However, there are two additional things that will make your application more secure.
Upvotes: 5