Detecting Ajax in PHP and making sure request was from my own website

22,222

Solution 1

Let you Controller

  • generate access token
  • store in session for later comparison

In your View

  • declare the access token as JS variable
  • send the token with each request

Back in your Controller

  • validate HTTP_X_REQUESTED_WITH
  • validate token

Check these security guidelines from OpenAjax.
Also, read the article on codinghorror.com Annie linked.

Solution 2

You can check the HTTP_REFERRER, but not all browsers set it. The best way is to write a wrapper for your ajax calls on the JavaScript side which sends part of document.cookie back to the server--only your domain has access to the cookie. You can compare the cookie in the request headers with the cookie in the AJAX call in php.

In response to, "does it even matter, these days"--YES, it does! Read this.

Solution 3

Regarding your last question: "Does it even matter, in these days?" This is a case by case question. If the ajax request is doing something that does not require security (e.g. loading latest stock quotes) then it really doesn't matter IMHO. If the request is loading information that should be secured (e.g. returning identifying information or doing something on the server) then you should treat it as such.

I personally don't use the server variables to know when something is an ajax request. Instead I just add a query parameter to the ajax call (e.g. http://domain.com/?ajax=true). If I need to secure the ajax call then I would use the same methods as securing a regular page request (using both client and server). As Lucas Oman pointed out, anything on the client side can be faked. Bottom line don't trust any request even if you think it is coming from your site or database. Always follow the mantra "filter input - escape output".

Solution 4

David Walsh has a good solution

/* decide what the content should be up here .... */
$content = get_content(); //generic function;

/* AJAX check  */
if(!empty($_SERVER['HTTP_X_REQUESTED_WITH']) && strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) == 'xmlhttprequest') {
    /* special ajax here */
    die($content);
}

/* not ajax, do more.... */

Solution 5

Really, the most secure way to do this is to, as you suggested, use server-side sessions, as these cannot be crafted as cookies can.

Granted, someone can still hijack a session ID, but if you also store the user's IP address in their session and check it on each request, you can weed out a lot of hijacks. Only someone on the same LAN or proxy could hijack it.

Any other method mentioned--cookies, javascript, http referer--depends on client-side data, which is insecure and should always be suspected of being fake, forged, hijacked and maliciously constructed.

Share:
22,222

Related videos on Youtube

Yossi
Author by

Yossi

Updated on March 10, 2021

Comments

  • Yossi
    Yossi about 3 years

    I use my PHP back-end to detect AJAX requests by checking for a value in $_SERVER['HTTP_X_REQUESTED_WITH'].

    This gives me a reliable detection, making sure the request is made utilizing AJAX techniques.

    How can I make sure the request came from my own domain, and not an external domain/robot?

    www.example.com/ajax?true could allow anyone to make an AJAX call and cut the information.

    I could make sessions for everyone that enters my website normally, and then allow AJAX calls.. but that can be faked too.

    Does it even matter these days?

  • Rik Heywood
    Rik Heywood over 14 years
    This will give you what you need, but is not reliable, as the browser can put anything it likes in this. It is easy to fake, so the value can not be truly trusted. Fundamentally, you can not guarantee that the request came from anywhere, even if you issue tokens to the page and make your ajax requests pass the token back to you. Really, you have to ask yourself, does it matter?
  • mr-sk
    mr-sk over 14 years
    @Jonathan Sampson - I can catch the request in something like BurpSuite and change the HTTP_REFERER...so I dont think that's the solution. I can also do it in a script w/curl.
  • Xeoncross
    Xeoncross over 14 years
    +1 Since browser-based AJAX requests send cookies with each request - all you need is to add the a token check on each page.
  • Xeoncross
    Xeoncross over 14 years
    That has nothing to do with securing the request.
  • zombat
    zombat over 14 years
    I'm not sure this method would accomplish anything except security through obscurity. It's trivial to craft a web request, which is all an Ajax request is. It's just slightly less trivial to examine the Javascript wrapper and add whatever it would be adding. All this does is emulate a session token.
  • zombat
    zombat over 14 years
    I guess it might keep robots at bay, if they didn't use Javascript. But it woudln't make the request secure, per se.
  • Annie
    Annie over 14 years
    This method requires access to the session cookie. If a robot has access to the session cookie, then yes, the site is not secure. But you also have bigger problems than XSRF!
  • Quentin
    Quentin over 14 years
    Yes, yes, yes. Jim is talking sense. Listen to him.
  • Yossi
    Yossi over 14 years
    I read the blog article, it was extremely informative since this subject is new to me. From my checkups, CakePHP does that automatically, and resets the cookie if the browser has been restarted. That should handle registered users. What happens, in public areas against robots, going around? example.com/blog/title1?ajax=true | example.com/blog/title2?ajax=true About "does it matter nowadays", I guess that I simply can't stop a robot from crawling easily through all of my public areas.
  • Annie
    Annie over 14 years
    About public areas, you can use spam prevention techniques like captchas: en.wikipedia.org/wiki/CAPTCHA
  • Quamis
    Quamis over 14 years
    Agree, and one can change the way session are generated so they'll be more secure.
  • thomasrutter
    thomasrutter almost 12 years
    From my understanding, @zombat's point is still a valid one. We're not talking about preventing CSRF here, we're talking about preventing robots from accessing features which you only want available to your own Javascript, which is a different problem. The solution given here would still allow a robot to request the containing page, be given its own cookie, and use that cookie to fetch information from your app using those features you only intended your own Javascript to use. The cookie doesn't prevent this scenario. As far as I can fathom, there is no 100% reliable way of solving this.
  • thomasrutter
    thomasrutter almost 12 years
    You simply have to assume that anything that you allow your Javascript to access for a non-authenticated user, any foreign script could also access. So you cannot design your internal AJAX API to give away any information or perform any actions for unauthenticated users that you wouldn't want an external robot also being able to view/perform.
  • Stijn de Witt
    Stijn de Witt almost 10 years
    Anyone can add HTTP headers to a request.
  • Quentin
    Quentin over 9 years
    That won't stop a robot hitting the API directly. It can get an access token just like your JavaScript app can.
  • frostymarvelous
    frostymarvelous over 8 years
    This is the best answer. You can never be sure the source of a request to your site. Even if the request is authenticated, a user could be using a bot and valid credentials or even just pulling their cookie out of the browser.
  • Yossi
    Yossi about 8 years
    I wouldn't use the session ID for security reasons. It's recommended generating a random token for every request, that is saved in your session. Read this: docs.phalconphp.com/en/latest/reference/…
  • Delmontee
    Delmontee over 6 years
    While this would work because ajax is posting the data (not using get), anybody could still see the values by just looking at the javascript. they could then copy the data, create their own form and post it to the same location. A check on the $_SERVER['HTTP_REFERER'] would help prevent that, as would a check that the data was posted via ajax but it's still not totally secured. Is there a better way?