0

My website presents a paid (not free) API. So I need to identify all incoming ajax calls and reject unknown requests. In other word, I only want to return a JSON result to:

  • the request comes from my own website
  • the request comes from a person who has paid the cost of API.

Here is my code:

$paid_ips = ['138.14.4.3', '32.16.6.1'];
$ip = $_SERVER['REMOTE_ADDR'];

if ( $ip == '::1' || in_array( $ip, $paid_ips) ) {
    // allowed
} else {
    // not allowed
}

As you know, $ip == '::1' determines my website's requests. Now I want to know is what I'm doing secure? Or there is a better approach to handle that?

Martin AJ
  • 6,261
  • 8
  • 53
  • 111
  • what about to save hash value in session and use here, in this case you don't need to compare your IP address all the time and it will work even if you change your server or some other reason. – Mubashar Iqbal Mar 25 '17 at 20:41
  • what about using a password and username.. most of the payment gateways are using like this...but dont' pass them using get method.. use https.. – Sugumar Venkatesan Mar 25 '17 at 20:42
  • Be careful, I've seen elsewhere that "$_SERVER['REMOTE_ADDR'] may not actually contain real client IP addresses", due to proxies and fake IPs. I found that topic about it: http://stackoverflow.com/questions/3003145/how-to-get-the-client-ip-address-in-php – Matiboux Mar 25 '17 at 20:43
  • @vSugumar an user and password can be good for users who paid api's cost. Ok fine. But how should I identify my website's requests? – Martin AJ Mar 25 '17 at 20:44
  • @MartinAJ just create an user for yourself/your own site? – Carsten Hagemann Mar 25 '17 at 20:45
  • @CarstenHagemann sounds good, but honestly I have no idea how should I do that. – Martin AJ Mar 25 '17 at 20:48
  • @CarstenHagemann - Or maybe just allow both a localhost IP and a username/password. Both may also be replaced by session hashes (or auth keys), stored in a cookie for the user (for example). – Matiboux Mar 25 '17 at 20:48

2 Answers2

1

What you're doing is secure: if the web server is properly configured a non-authorized user cannot access the service with a "fake" ip.

Is there a better approach to handle that? Yes

Give each authorized API consumer a unique, large, authorization key.

Then the API consumer will be requested to send the key with each request in the request's header. Something like:

Authorization: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

The API consumer have to set one more header value when making requests, but that's trivial.

On the other side authorizing API consumers by IP has many downsides. The main one is that each user must have a static IP address.


Edit: as I understand from the comments you web service is consumed directly by JavaScript client side (browser) code.

In this case the JavaScript code should send the ajax call to your API consumer server

The API consumer's server send the request to your web service (sending the token). Fetch the data. Finally send the retrieved data to the client as a response to the ajax call.

This way the auth token is not exposed in the JavaScript code. Not your token, nor the token of your clients.

Paolo
  • 15,233
  • 27
  • 70
  • 91
  • Ok good. I will remove checking the IP and focus on an *access-token* which should be checked per each request. all fine. But in this case, how can I authenticate my own website? I need to devote a token for my website which should be sent by every ajax request, right? ok, in this case that token should be written in the JS code which is visible for everybody. So eveybody can use the API as free (by copy/pasting the token belongs to my own website). any suggestion? – Martin AJ Mar 25 '17 at 20:57
  • 1
    @MartinAJ - You could use both of the two worlds: `if( $ip == '::1' || in_array( $authKey, $allowedAuthKeys) )`. Something like that. – Matiboux Mar 25 '17 at 20:58
  • @Matiboux what you're suggesting is reasonable, thank you. But I hardly think that is Paolo's mean. – Martin AJ Mar 25 '17 at 21:00
  • @MartinAJ - You can always give your own website an auth key. With a large key length (plz do large – but reasonable – length, to prevent quick brute-force attacks), you could generate a hella lot of keys. – Matiboux Mar 25 '17 at 21:03
  • @MartinAJ - the situation is a little bit more complicated then. see my edit – Paolo Mar 25 '17 at 21:06
  • oh my god ..! why I don't understand this? @Matiboux and Paolo. It's 4 hours I'm thinking about it, but still I don't know how exactly can I devote a token for my own website?! Look, I need to pass that token through js codes, also as you know js codes are client-side. So all people can see the token. Am I wrong? – Martin AJ Mar 25 '17 at 21:12
  • you cannot store token in the js code. even assuming the code is very obfuscated then the token would be sent clear in the header of each request. Not only the token of your website. The token of every client. – Paolo Mar 25 '17 at 21:15
  • believe me, in your approach, there isn't any different between ajax requests come from my own website and the ones some users send for fake. Well how can I understand should I response the request or not? – Martin AJ Mar 25 '17 at 21:21
0

sigh.
I don't think of any secure way to store token in a JS script. It's public, it's just awful.

The only way I got to secure you API would be to mix both username/password authentication and auth keys (tokens).
Let me explain the steps.

  1. Your client have to enter both a username and password in your web page. Submitting the form would trigger a JS script that will send the username and password to your server.

  2. Your script on your server will compare the username and password to those stored a database or whatever. If those match the stored ones, the script will create a random auth key, store it* (again, in a database or anything) and return it to the client.
    *The stored auth key must be long enough (24 character and +) and have to expire after a defined delay.

  3. The client will store it in a cookie or something (in order to stay "logged in"), and will have to provide the auth key every time a request is sent to the server: the server needs to check the auth key validity – the client identity – every time.

I don't think of anything else but that kind of authentication..
I hate JS for that kind of security problems..

But this way, client's username and password are not stored, and the auth key does expire.

PS: Please hash the user password before storing it on your server. That way, it would be technically impossible to know the original password. You can't forget the possibility of having your data stolen.

halfer
  • 19,824
  • 17
  • 99
  • 186
Matiboux
  • 112
  • 8