67

I would like to check server-side if a request to my php page is an ajax request or not.

I saw two ways to do this:

First way: sending a GET parameter in the request which tells the page that this is an AJAX request (=mypage.php?ajax)

mypage.php:

if(isset($_GET['ajax'])) {
    //this is an ajax request, process data here.
}

Second way: set a header to the xmlHttpRequest:

client-side js:

xmlHttpRequestObject.open(“GET”,url,true);
xmlHttpRequestObject.setRequestHeader('X-Requested-With', 'XMLHttpRequest');

mypage.php:

if (!empty($_SERVER['HTTP_X_REQUESTED_WITH']) && strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) == 'xmlhttprequest' ) {
    //request is ajax
}

The fact is, those two ways of doing it can easily be hacked, so it's not secure to check if i get an AJAX request like this.

How can i check if i'm receiving an AJAX request?

tshepang
  • 10,772
  • 21
  • 84
  • 127
BackSlash
  • 20,445
  • 19
  • 77
  • 124
  • 8
    Just keep in mind: "NEVER trust the client side". You cannot know from where a request is made. You could spoof/disguise/fake (almost) everything. – ChrisG Aug 15 '13 at 19:48
  • Possible duplicate of http://stackoverflow.com/questions/4301150/how-do-i-check-if-the-request-is-made-via-ajax-with-php – JRizz Aug 15 '13 at 19:49
  • @ChristianGärtner I know that, that's why i posted the question. I don't think there is a 100% safe way to do it, but i think there must be a way to make it tricky for "hackers" to fake the request (maybe managing it server-side) – BackSlash Aug 15 '13 at 19:50
  • @J.Robertson As written in the question, i tried to do it as is answered in the question you linked, but it can easily be spoofed. – BackSlash Aug 15 '13 at 19:51
  • "hackers" would see through any "tricky" stuff. there is literally NOTHING you can do to 100% reliably tell is an http request is "legit" or "faked". – Marc B Aug 15 '13 at 19:59
  • @MarcB Yes, but if it's tricky it will be harder for them to fake it. It's not 100% safe, there isn't a 100% safe way, but this could be a good start. – BackSlash Aug 15 '13 at 20:02
  • 1
    at best you can put a very minor speedbump in the "hacker's" path. just remember that ajax is just a perfectly normal http request. other than the fact that it tens to have been initiated by some javascsript as a background request within a web page, there is absolutely no standard method of deciding if it's ajax or something the user clicked on/submitted. the presence/abscence of the X headers is not a guarantee of anything – Marc B Aug 15 '13 at 20:04

8 Answers8

65

Here is the tutorial of achieving the result.

Example:

if(!empty($_SERVER['HTTP_X_REQUESTED_WITH']) && strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) == 'xmlhttprequest')
{    
  exit;    
}
continue;

This checks if the HTTP_X_REQUESTED_WITH parameter is not empty and if it's equal to xmlhttprequest, then it will exit from the script.

Panos Kal.
  • 11,714
  • 8
  • 60
  • 73
Volodymyr
  • 1,377
  • 2
  • 12
  • 19
  • 4
    AFAIK `$_SERVER['HTTP_X_REQUESTED_WITH']` can be spoofed, so it's not you won't know for sure that it is an AJAX request. Anyway this is a very bad answer, check [How To Answer](http://stackoverflow.com/questions/how-to-answer): ***Provide context for links*** - *A link to a potential solution is always welcome, but please add context around the link so your fellow users will have some idea what it is and why it’s there.* ***Always quote the most relevant part of an important link, in case the target site is unreachable or goes permanently offline.*** – BackSlash Jan 28 '14 at 18:21
  • 6
    This answer is EXTREMELY relevant now because the facebook android app sends with `HTTP_X_REQUESTED_WITH=com.facebook.katana.` Adding in the last check `== 'xmlhttprequest'` You need to implement this check of type witht he new facebook browser, otherwise strange things can happen. – Tschallacka Mar 17 '16 at 09:28
  • $_SERVER['HTTP_X_REQUESTED_WITH'] and be undefined – boctulus Jan 16 '19 at 20:04
  • I often use this as an additional hurdle for scrapers to contend with, it's not foolproof but it's effective in some cases... As `$_SERVER['HTTP_X_REQUESTED_WITH']` may be undefined I recommend a slight edit. `if(!isset($_SERVER['HTTP_X_REQUESTED_WITH']) || strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) != 'xmlhttprequest')`... – Andy Gee Nov 10 '20 at 02:22
38

There is no sure-fire way of knowing that a request was made via Ajax. You can never trust data coming from the client. You could use a couple of different methods but they can be easily overcome by spoofing.

Wayne Whitty
  • 18,505
  • 4
  • 38
  • 64
  • 1
    Yes. I'm pretty sure there isn't a 100% safe way to do it. But i think that there must be a way to make it tricky for "hackers" to fake the request (maybe managing it server side?) – BackSlash Aug 15 '13 at 19:52
  • 5
    @BackSlash You could make it tricky, but that would be security through obscurity. Personally, I wouldn't rely on it. Your best bet would probably be tokens that change per-request. Or a captcha. – Wayne Whitty Aug 15 '13 at 19:55
  • I was thinking about cookies, are they sent with an ajax request? – BackSlash Aug 15 '13 at 19:58
  • An Ajax request is basically just a HTTP request that is sent without the user having to leave the page. Anything you can do with a HTTP request can also be done in an Ajax request :) Wouldn't rely on cookies either though! – Wayne Whitty Aug 15 '13 at 20:00
  • @BackSlash: I found a nice article, have a look: http://abhinavsingh.com/blog/2009/10/web-security-using-crumbs-to-protect-your-php-api-ajax-call-from-cross-site-request-forgery-csrfxsrf-and-other-vulnerabilities/ – Syed Qarib Dec 01 '14 at 12:28
  • `if(isset($_SERVER['HTTP_CF_CONNECTING_IP'])) { $_SERVER['REMOTE_ADDR'] = $_SERVER['HTTP_CF_CONNECTING_IP']; }` – Krii Feb 10 '16 at 15:18
  • 1
    but what is the security risks here? the headers can be spoofed ... so what? it is just an ajax request that is accessible from the public ... spoof or not... – yeahman Nov 04 '18 at 07:10
  • @yeahman That would depend on the purpose of the check. e.g. Trying to prevent non-Ajax requests. – Wayne Whitty Aug 20 '20 at 12:19
12

From PHP 7 with null coalescing operator it will be shorter:

$is_ajax = 'xmlhttprequest' == strtolower( $_SERVER['HTTP_X_REQUESTED_WITH'] ?? '' );
luckydonald
  • 3,930
  • 2
  • 27
  • 46
Emeszenes
  • 121
  • 1
  • 2
11

Set a session variable for every page on your site (actual pages not includes or rpcs) that contains the current page name, then in your Ajax call pass a nonce salted with the $_SERVER['SCRIPT_NAME'];

<?php
function create_nonce($optional_salt='')
{
    return hash_hmac('sha256', session_id().$optional_salt, date("YmdG").'someSalt'.$_SERVER['REMOTE_ADDR']);
}
$_SESSION['current_page'] = $_SERVER['SCRIPT_NAME'];
?>

<form>
  <input name="formNonce" id="formNonce" type="hidden" value="<?=create_nonce($_SERVER['SCRIPT_NAME']);?>">
  <label class="form-group">
    Login<br />
    <input name="userName" id="userName" type="text" />
  </label>
  <label class="form-group">
    Password<br />
    <input name="userPassword" id="userPassword" type="password" />
  </label>
  <button type="button" class="btnLogin">Sign in</button>
</form>
<script type="text/javascript">
    $("form.login button").on("click", function() {
        authorize($("#userName").val(),$("#userPassword").val(),$("#formNonce").val());
    });

    function authorize (authUser, authPassword, authNonce) {
        $.ajax({
          type: "POST",
          url: "/inc/rpc.php",
          dataType: "json",
          data: "userID="+authUser+"&password="+authPassword+"&nonce="+authNonce
        })
        .success(function( msg ) {
            //some successful stuff
        });
    }
</script>

Then in the rpc you are calling test the nonce you passed, if it is good then odds are pretty great that your rpc was legitimately called:

<?php
function check_nonce($nonce, $optional_salt='')
{
    $lasthour = date("G")-1<0 ? date('Ymd').'23' : date("YmdG")-1;
    if (hash_hmac('sha256', session_id().$optional_salt, date("YmdG").'someSalt'.$_SERVER['REMOTE_ADDR']) == $nonce || 
        hash_hmac('sha256', session_id().$optional_salt, $lasthour.'someSalt'.$_SERVER['REMOTE_ADDR']) == $nonce)
    {
        return true;
    } else {
        return false;
    }
}

$ret = array();
header('Content-Type: application/json');
if (check_nonce($_POST['nonce'], $_SESSION['current_page']))
{
    $ret['nonce_check'] = 'passed';
} else {
    $ret['nonce_check'] = 'failed';
}
echo json_encode($ret);
exit;
?>

edit: FYI the way I have it set the nonce is only good for an hour and change, so if they have not refreshed the page doing the ajax call in the last hour or 2 the ajax request will fail.

Hassaan
  • 6,355
  • 5
  • 25
  • 44
GovCoder
  • 111
  • 1
  • 2
  • 1
    This is more a CSRF Protection than a way to decide if it was a regular GET/POST request or an ajax request. – Sebi2020 Sep 20 '18 at 01:10
7

This function is using in yii framework for ajax call check.

public function isAjax() {
        return isset($_SERVER['HTTP_X_REQUESTED_WITH']) && $_SERVER['HTTP_X_REQUESTED_WITH'] === 'XMLHttpRequest';
}
Nanhe Kumar
  • 12,767
  • 3
  • 67
  • 60
4
$headers = apache_request_headers();
$is_ajax = (isset($headers['X-Requested-With']) && $headers['X-Requested-With'] == 'XMLHttpRequest');
revoke
  • 357
  • 3
  • 8
  • 1
    Some headers can easily be spoofed. `X-Requested-With` is one of them, as it's set by the client. That's why I don't want to use it. – BackSlash Jun 25 '14 at 13:41
  • 1
    Yes, it's not safe, but if you simply need to decide on the format of the answer, you can use it. – revoke Jul 02 '14 at 10:17
  • This NOT work with android facebook app, set the x-request-with to com.facebook.katana ! – Blackfire Aug 17 '17 at 23:42
1

Try below code snippet

if(!empty($_SERVER['HTTP_X_REQUESTED_WITH']) 
   && strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) == 'xmlhttprequest') 
  {
    /* This is one ajax call */
  }
Sani Kamal
  • 931
  • 12
  • 22
-6

You could try using a $_SESSION variable to make sure that a request was made from a browser. Otherwise, you could have the request sent through a database or file [server-side].