My app consists of several PHP endpoints which are accessible via AJAX. The problem is they are also accessible via anyone who makes an HTTP request to the same endpoint. I can add checks for HTTP_X_REQUESTED_WITH
and HTTP_REFERER
as specified in this answer, but these can be spoofed. I could add a secret key that needs to be posted with the request, but anyone viewing the javascript and/or the console would be able to see this key. What is the solution here?
-
What are you worried about? CSRF? If so, a token is the answer. It doesn't matter if it's Ajax or not, though. – Dennis Hackethal Feb 21 '16 at 20:57
-
1No, I just don't want (e.g.) an endpoint such as "deleteJob" to be called by a malicious person/tool, only by the actual website on the server that the endpoint resides on. – GluePear Feb 21 '16 at 20:59
-
If the endpoint is public, anyone will be able to invoke it, Ajax or not. You'll need authentication. – Dennis Hackethal Feb 21 '16 at 21:00
-
1Yes, I suppose my question is: how can client-side authentication work when everyone is able to see the username/password passed by the client (by looking in the console, e.g.)? – GluePear Feb 21 '16 at 21:01
4 Answers
People often think that because they're using Ajax requests regular sessions
don't work. They do.
If you have an endpoint to delete something from the database that's visible in the source code, such as:
example.com/user/1/delete
You can protect this request from non authenticated users the same way you would when using a non Ajax HTTP request in the browser. Using sessions. If the user has the privileges to remove users, this route will work, otherwise return an error (or do nothing).
You can also protect an API using OAuth. There's a great document here that explains how it works: http://tatiyants.com/using-oauth-to-protect-internal-rest-api/

- 13,662
- 12
- 66
- 115

- 8,346
- 16
- 65
- 121
-
session won't work cross domain. For instance if you have your app running on `app.example.com` and your API on `api.example.com` you will not have sessions available. – Steven M Jun 29 '16 at 12:09
Most of the answers are not helpful if you have your app and your api on separate domains for example app.example.com
and api.example.com
- in that case sessions won't work and you would have to turn to OAuth which is quite a big hammer for such a simple problem.
Here is what I would do:
I assume you have users in a database and a unique identifier like user_id=12345
. I also assume that you have your Jobs in a Database and they also have unique ID's like job_id=6789
.
First on app.example.com
you encrypt both IDs with something fast and easy like Blowfish:
$secret_uid = mcrypt_encrypt(MCRYPT_BLOWFISH, "your_secret", strval($user_id));
$secret_jid = mcrypt_encrypt(MCRYPT_BLOWFISH, "your_secret", strval($job_id));
I assume your endpoint would work somewhat like this:
api.example.com/jobs/delete/<job_id>/<user_id>
so now from Ajax you call that endpoint, but instead of calling with plain IDs
api.example.com/jobs/delete/6789/12345
you call it with the encrypted IDs:
api.example.com/jobs/delete/6A73D5B557C622B3/57F064C07F83644F
On the API side of your software you decrypt the parameters:
$jid = mcrypt_decrypt(MCRYPT_BLOWFISH, "your_secret", <param_1>);
$uid = mcrypt_decrypt(MCRYPT_BLOWFISH, "your_secret", <param_2>);
Now you can search your db for uid
and jid
and perform whichever task you were planning to do. Make sure that a user can only delete his own jobs of course.
I admit this is not a 100% solution, but it leaves an attacker with a lot of guess work - he would have to guess the user_id and a matching job_id, the encryption algo and your secret. It does not protect against running millions of brute force attempts to guess a matching pair, but it put's the odds in your favor (and you should have some sort of quota limitation protection of your endpoints anyway).
Good luck!

- 574
- 4
- 20
There isn't one. If you give someone some data, then they can process it in whatever way they like. You can't control what happens to it after it leaves your server.
Likewise, you can't control what data they send to the endpoint.

- 914,110
- 126
- 1,211
- 1,335
-
Precisely. Anything you give to/from the browser can be used, modified, and manipulated by any attacker with brains. – ceejayoz Feb 21 '16 at 20:56
-
So say I have an endpoint called "deleteJob" which I want to have accessible to the front-end / client, there is *no way* to secure it? – GluePear Feb 21 '16 at 20:58
-
There is, through authentication and then perhaps authorization rules. Authentication can be done through a cookie or token stored on the client that the client needs to send along. – Dennis Hackethal Feb 21 '16 at 20:59
-
@GluePear — Username and password or any other kind of auth/authz, and you can limit which jobs a given user can delete. – Quentin Feb 21 '16 at 20:59
-
@Quentin - but these are visible to anyone viewing the client-side code, no? – GluePear Feb 21 '16 at 21:02
-
@GluePear — You need to authorise the *user* not the *page*. So no, they wouldn't be visible to anyone viewing the client side code, whomever is running the client side code would have to type them in (or use OAuth or whatever). – Quentin Feb 21 '16 at 21:03
-
@Quentin - Understood, thank you. This is more the answer to my question than your actual answer. If you care to edit it I will mark it correct. – GluePear Feb 21 '16 at 21:05
it is very important for a developer to put authentication for API or web services. dchacke and BugHunterUK has given perfect answers, I just want show you simple code I use to make very simple and easy to use authentication.
Adding Session for the authentication
you can add session, and session timeout for your APIs so, only your app can use this, you can start session when front page of your app is loaded, you can set timeouts and also restrict the different service for different users by sessions.
General Idea how to do that
<?php
if(!empty($_SESSION['api_session']) && $_SESSION['api_session'] == 'usertype'){
//usertype comprise of what access you want to give
//guest, registered user, stack holder, admin etc.
...
header('Content-Type:application/json;');
echo json_encode($output);
}

- 607
- 4
- 16