如何用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请求?
只要牢记。"永远不要相信客户端"。你无法知道一个请求是从哪里发出的。你可以欺骗/伪装/伪造(几乎)一切。
- 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
,那么它将退出脚本。
这个答案现在非常重要,因为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发出的。你永远无法相信来自客户端的数据。你可以使用一些不同的方法,但它们很容易被欺骗所克服。
是的,我很确定没有一个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'] ?? '' );
$_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请求就会失败。
这更像是一种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';
}
#6楼
得票数 5
$headers = apache_request_headers();
$is_ajax = (isset($headers['X-Requested-With']) && $headers['X-Requested-With'] == 'XMLHttpRequest');
有些头信息很容易被欺骗。
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 */
}
#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"));
#9楼
得票数 -7
你可以尝试使用一个$_SESSION
变量来确保请求是从浏览器发出的。否则,你可以让请求通过数据库或文件[服务器端]发送。
你如何从客户那里定义
$_SESSION
?
- Naman 2016-07-24