jeff
jeff

Reputation: 13653

How to make sure AJAX is called by JavaScript?

I asked a similar question before, and the answer was simply:

if JavaScript can do it, then any client can do it.

But I still want to find out a way do restrict AJAX calls to JavaScript.

The reason is :

I'm building a web application, when a user clicks on an image, tagged like this:

<img src='src.jpg' data-id='42'/>

JavaScript calls a PHP page like this:

$.ajax("action.php?action=click&id=42");

then action.php inserts rows in database.

But I'm afraid that some users can automate entries that "clicks" all the id's and such, by calling necessary url's, since they are visible in the source code.

How can I prevent such a thing, and make sure it works only on click, and not by calling the url from a browser tab?

p.s.

I think a possible solution would be using encryption, like generate a key on user visit, and call the action page with that key, or hash/md5sum/whatever of it. But I think it can be done without transforming it into a security problem. Am I right ? Moreover, I'm not sure this method is a solution, since I don't know anything about this kind of security, or it's implementation.

Upvotes: 2

Views: 398

Answers (5)

GordonM
GordonM

Reputation: 31730

Your question isn't really "How can I tell AJAX from non-AJAX?" It's "How do I stop someone inflating a score by repeated clicks and ballot stuffing?"

In answer to the question you asked, the answer you quoted was essentially right. There is no reliable way to determine whether a request is being made by AJAX, a particular browser, a CURL session or a guy typing raw HTTP commands into a telnet session. We might see a browser or a script or an app, but all PHP sees is:

GET /resource.html HTTP/1.1
host:www.example.com

If there's some convenience reason for wanting to know whether a request was AJAX, some javascript libraries such as jQuery add an additional HTTP header to AJAX requests that you can look for, or you could manually add a header or include a field to your payload such as AJAX=1. Then you can check for those server side and take whatever action you think should be made for an AJAX request.

Of course there's nothing stopping me using CURL to make the same request with the right headers set to make the server think it's an AJAX request. You should therefore only use such tricks where whether or not the request was AJAX is of interest so you can format the response properly (send a HTML page if it's not AJAX, or JSON if it is). The security of your application can't rely on such tricks, and if the design of your application requires the ability to tell AJAX from non-AJAX for security or reliability reasons then you need to rethink the design of your application.

In answer to what you're actually trying to achieve, there are a couple of approaches. None are completely reliable, though. The first approach is to deposit a cookie on the user's machine on first click, and to ignore any subsequent requests from that user agent if the cookie is in any subsequent requests. It's a fairly simple, lightweight approach, but it's also easily defeated by simply deleting the cookie, or refusing to accept it in the first place.

Alternatively, when the user makes the AJAX request, you can record some information about the requesting user agent along with the fact that a click was submitted. You can, for example store a hash (stored with something stronger than MD5!) of the client's IP and user agent string, along with a timestamp for the click. If you see a lot of the same hash, along with closely grouped timestamps, then there's possibly abuse being attempted. Again, this trick isn't 100% reliable because user agents can see any string they want as their user agent string.

Upvotes: 2

mguimard
mguimard

Reputation: 1891

This solution may need more reflexion but might do the trick

You could use tokens as stated in Slicedpan's answer. When serving your page, you would generate uuids for each images and store them in session / database.

Then serve your html as

<img src='src.jpg' data-id='42' data-uuid='uuidgenerated'/>

Your ajax request would become

$.ajax("action.php?action=click&uuid=uuidgenerated");

Then on php side, check for the uuid in your memory/database, and allow or not the transaction. (You can also check for custom headers sent on ajax as stated in other responses)

You would also need to purge uuids, on token lifetime, on window unload, on session expired...

This method won't allow you to know if the request comes from an xhr but you'll be able to limit their number.

Upvotes: 0

Slicedpan
Slicedpan

Reputation: 5015

I'm not sure there is a 100% secure answer. A combination of a server generated token that is inserted into a hidden form element and anti-automation techniques like limiting the number of requests over a certain time period is the best thing I can come up with.

[EDIT] Actually a good solution would be to use CAPTCHAS

Upvotes: 2

ciruvan
ciruvan

Reputation: 5213

You could, for example, implement a check if the request is really done with AJAX, and not by just calling the URL.

if(!empty($_SERVER['HTTP_X_REQUESTED_WITH']) && strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) == 'xmlhttprequest') {
    // Yay, it is ajax!
} else {
    // no AJAX, man..
}

Upvotes: 0

Garry
Garry

Reputation: 595

Use post method instead of get.Read the documentation here http://api.jquery.com/jQuery.post/ to learn how to use post method in jquery

Upvotes: 0

Related Questions