如何用PHP检查请求是否是AJAX请求?

回答 9 浏览 14.3万 2013-08-15

我想在服务器端检查对我的php页面的请求是否是一个ajax请求。

我看到有两种方法可以做到这一点。

第一种方式:在请求中发送GET参数,告诉页面这是一个AJAX请求(=mypage.php?ajax

mypage.php:

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

第二种方式:给xmlHttpRequest设置一个header:

客户端的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
}

事实上,这两种方式很容易被黑客攻击,因此,如果我收到这样的AJAX请求,检查起来并不安全。

我怎样才能检查我是否收到了AJAX请求?

BackSlash 提问于2013-08-15
只要牢记。"永远不要相信客户端"。你无法知道一个请求是从哪里发出的。你可以欺骗/伪装/伪造(几乎)一切。ChrisG 2013-08-15
@ChristianGärtner 我知道,这就是我发布这个问题的原因。我不认为有100%安全的方法,但我认为一定有办法让 "黑客 "难以伪造请求(也许是在服务器端管理)。BackSlash 2013-08-15
@J.Robertson 正如问题中所写的那样,我试图按照你所链接的问题中的答案来做,但它很容易被欺骗。BackSlash 2013-08-15
你最多可以在 "黑客 "的道路上设置一个非常小的障碍。请记住,ajax只是一个完全正常的http请求。除了它似乎是由一些javascsript作为网页中的背景请求发起的,绝对没有标准的方法来决定它是ajax还是用户点击/提交的东西。Marc B 2013-08-15
9 个回答
#1楼
得票数 73

示例来自存档的教程

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

这将检查HTTP_X_REQUESTED_WITH参数是否为空,如果它等于xmlhttprequest,那么它将退出脚本。

Volodymyr 提问于2014-01-28
ashleedawg 修改于2022-09-02
这个答案现在非常重要,因为facebook的安卓应用在发送时HTTP_X_REQUESTED_WITH=com.facebook.katana.加入了最后的检查== 'xmlhttprequest',你需要在新的facebook浏览器中实现这种类型的检查,否则就会发生奇怪的事情。Tschallacka 2016-03-17
$_SERVER['HTTP_X_REQUESTED_WITH']和未定义的。boctulus 2019-01-16
我经常用这个作为刮削者要面对的额外障碍,它不是万无一失的,但在某些情况下是有效的......由于$_SERVER['HTTP_X_REQUESTED_WITH']可能是未定义的,我建议稍作修改。if(!isset($_SERVER['HTTP_X_REQUESTED_WITH']) || strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) != 'xmlhttprequest')...Andy Gee 2020-11-10
你好,来自2021年,如果用户使用jQuery ajax,如.ajax().post(),这段代码可以正常工作,但如果使用vanilla JS XMLHttpRequest(),或.fetch(),他们就不会再发送这个头。vee 2021-06-13
#2楼 已采纳
得票数 41

没有万无一失的方法可以知道一个请求是通过Ajax发出的。你永远无法相信来自客户端的数据。你可以使用一些不同的方法,但它们很容易被欺骗所克服。

Wayne Whitty 提问于2013-08-15
是的,我很确定没有一个100%安全的方法来做到这一点。但我认为,一定有办法让 "黑客 "难以伪造请求(也许可以在服务器端进行管理?)BackSlash 2013-08-15
@BackSlash 你可以让它变得很棘手,但这将是通过隐蔽性实现的安全。就个人而言,我不会依赖它。你最好的选择可能是按请求改变的令牌。或者使用验证码。Wayne Whitty 2013-08-15
我正在考虑cookies的问题,它们是否与ajax请求一起发送?BackSlash 2013-08-15
Ajax请求基本上只是一个HTTP请求,它的发送不需要用户离开页面。任何你能用HTTP请求做的事情也能在Ajax请求中完成:)不过也不会依赖cookie!Wayne Whitty 2013-08-15
但是,这里有什么安全风险呢?头文件可以被欺骗......那又怎么样呢? 它只是一个可以从公众那里访问的ajax请求......无论是否欺骗......。yeahman 2018-11-04
#3楼
得票数 14

从PHP7开始,有了null凝聚运算符,它将会更短。

$is_ajax = 'xmlhttprequest' == strtolower( $_SERVER['HTTP_X_REQUESTED_WITH'] ?? '' );
Emeszenes 提问于2018-05-11
luckydonald 修改于2020-03-23
$_SERVER['HTTP_X_REQUESTED_WITH']经常是未被定义的。Andy Gee 2020-11-10
#4楼
得票数 11

为您网站上包含当前页面名称的每个页面(实际页面不包括或 rpcs)设置一个会话变量,然后在您的 Ajax 调用中传递一个用 $_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>

然后在你调用的rpc中测试你所传递的nonce,如果它是好的,那么你的rpc被合法调用的几率就很大。

<?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;
?>

编辑:参考我的设置方式,nonce只在一小时内有效,所以如果他们在过去一两个小时内没有刷新页面进行ajax调用,那么ajax请求就会失败。

GovCoder 提问于2013-11-18
Hassaan 修改于2019-02-05
这更像是一种CSRF保护,而不是决定它是一个普通的GET/POST请求还是ajax请求的方法。Sebi2020 2018-09-20
#5楼
得票数 9

这个函数被Yii的PHP框架用来检查AJAX调用。

public function isAjax() {
  return isset($_SERVER['HTTP_X_REQUESTED_WITH']) && $_SERVER['HTTP_X_REQUESTED_WITH'] === 'XMLHttpRequest';
}
Nanhe Kumar 提问于2018-01-17
Nanhe Kumar 修改于2021-10-27
#6楼
得票数 5
$headers = apache_request_headers();
$is_ajax = (isset($headers['X-Requested-With']) && $headers['X-Requested-With'] == 'XMLHttpRequest');
revoke 提问于2014-06-25
有些头信息很容易被欺骗。X-Requested-With就是其中之一,因为它是由客户端设置的。这就是为什么我不想使用它。BackSlash 2014-06-25
是的,这并不安全,但如果你只是需要决定答案的格式,你可以使用它。revoke 2014-07-02
这在安卓facebook应用中不适用,请将x-request-with设置为com.facebook.katana !Blackfire 2017-08-17
#7楼
得票数 1

尝试以下代码片断

if(!empty($_SERVER['HTTP_X_REQUESTED_WITH']) 
   && strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) == 'xmlhttprequest') 
  {
    /* This is one ajax call */
  }
Sani Kamal 提问于2019-08-09
#8楼
得票数 1

尝试使用HTTP_SEC

  if (!array_key_exists("HTTP_SEC_FETCH_SITE",$_SERVER) || $_SERVER["HTTP_SEC_FETCH_SITE"] != "same-origin")
    die(header("HTTP/1.1 404 Not Found"));
  if (!array_key_exists("HTTP_SEC_FETCH_MODE",$_SERVER) || $_SERVER["HTTP_SEC_FETCH_MODE"] != "cors")
    die(header("HTTP/1.1 404 Not Found"));
  if (!array_key_exists("HTTP_SEC_FETCH_DEST",$_SERVER) || $_SERVER["HTTP_SEC_FETCH_DEST"] != "empty")
    die(header("HTTP/1.1 404 Not Found"));
Smar ts 提问于2022-07-17
#9楼
得票数 -7

你可以尝试使用一个$_SESSION变量来确保请求是从浏览器发出的。否则,你可以让请求通过数据库或文件[服务器端]发送。

Static Cast 提问于2013-08-15
你如何从客户那里定义$_SESSIONNaman 2016-07-24