在 PHP 中检测 Ajax 并确保请求来自我自己的网站

Posted

技术标签:

【中文标题】在 PHP 中检测 Ajax 并确保请求来自我自己的网站【英文标题】:Detecting Ajax in PHP and making sure request was from my own website 【发布时间】:2010-12-29 13:32:53 【问题描述】:

我使用我的 php 后端通过检查 $_SERVER['HTTP_X_REQUESTED_WITH'] 中的值来检测 AJAX 请求。

这为我提供了可靠的检测,确保使用 AJAX 技术发出请求。

如何确保请求来自我自己的域,而不是外部域/机器人?

www.example.com/ajax?true 可以允许任何人进行 AJAX 调用并剪切信息。

我可以为正常进入我网站的每个人创建会话,然后允许 AJAX 调用..但这也可以伪造。

这些天还重要吗?

【问题讨论】:

【参考方案1】:

使用 google recaptcha...它为您的 Ajax 生成一个令牌,然后在服务器端您可以验证该令牌...

【讨论】:

【参考方案2】:

您可以检查 HTTP_REFERRER,但并非所有浏览器都设置它。最好的方法是在 javascript 端为你的 ajax 调用编写一个包装器,它将 document.cookie 的一部分发送回服务器——只有你的域可以访问 cookie。您可以将请求标头中的 cookie 与 php 中的 AJAX 调用中的 cookie 进行比较。

回应“这几天有没有关系”——是的,有关系! Read this.

【讨论】:

我不确定这种方法能否通过默默无闻来实现安全性之外的任何事情。制作 Web 请求很简单,这就是 Ajax 请求的全部内容。检查 Javascript 包装器并添加它要添加的任何内容都不是那么简单。所有这一切都是模拟一个会话令牌。 我想如果机器人不使用 Javascript,它可能会阻止机器人。但它本身不会使请求安全 此方法需要访问会话 cookie。如果机器人可以访问会话 cookie,那么是的,该站点不安全。但是你也有比 XSRF 更大的问题! 我阅读了这篇博客文章,它提供了非常丰富的信息,因为这个主题对我来说是新的。根据我的检查,CakePHP 会自动执行此操作,并在重新启动浏览器时重置 cookie。那应该处理注册用户。会发生什么,在公共场所反对机器人,四处走动? example.com/blog/title1?ajax=true | example.com/blog/title2?ajax=true 关于“现在重要吗”,我想我根本无法阻止机器人轻松爬过我所有的公共区域。 关于公共区域,您可以使用验证码等垃圾邮件预防技术:en.wikipedia.org/wiki/CAPTCHA【参考方案3】:

使用 POST 会话安全请求:

在网页内部(例如 index.php)我们需要存储 sessionid

<?php
// Create Session
$session = session_id();
if(empty($session)) session_start();
?>
<head>
...
<script type="text/javascript">
  sid = '<?php echo session_id(); ?>';
</script>
<script type="text/javascript" src="ajaxrequest.js"></script>
...
</head>

ajax 请求 (ajaxrequest.js)

/* simple getAjax function 
 * @param $url       request url
 * @param $param     parameter (dont use ?)
 * @param callback  function on success
 */
var spinnerid = '#spinner'; // Spinner as long ajax requests running
$(document).ajaxStart(function()  $(spinnerid).show(); );
$(document).ajaxStop(function()  $(spinnerid).hide(); );
function getAjax( url, param, callback ) 
    var data = null;
    url += "?sid=" + sid + "&" + param;
    $.ajax(
        url: url,
        method: "POST", // uncomment to use GET, POST is secured by session
        cache: false,
        async: true,
        success : function(data)
      callback(data);
    ,


getAjax( 'http://domain.com/', 'data=foo', function( data ) 
 // do stuf with data 
 var jsonobj = eval("(" + data + ")");
 var data = jsonobj[0][ 'data' ];
);

负责的php端:

if( isset( $_GET['sid'] ) ) $client_sid = $_GET['sid'];

if( session_id() == null ) session_start();

if( session_id() != $client_sid ) 
    // noID or wrongID, redirect to mainindex
    ignore_user_abort(true);
    header( "HTTP/1.1 403 Forbidden" );
    header("Connection: close", true);
    exit;
 else 

    // get data
    if( isset( $_GET['data'] ) ) 
        $data = $_GET['data'];
     else if( isset( $_POST['data'] ) ) 
        $data = $_POST['data'];
     else 
        $data = null;
    

    // do stuff with data

    // return data as json
    $resp[0]['data'] = $data; 
    print_r( json_encode( $resp ) );

【讨论】:

出于安全原因,我不会使用会话 ID。建议为每个请求生成一个随机令牌,该令牌保存在您的会话中。阅读:docs.phalconphp.com/en/latest/reference/… 虽然这会起作用,因为 ajax 正在发布数据(不使用 get),但任何人仍然可以通过查看 javascript 来查看值。然后他们可以复制数据,创建自己的表单并将其发布到同一位置。检查 $_SERVER['HTTP_REFERER'] 将有助于防止这种情况发生,检查数据是否通过 ajax 发布但仍不完全安全。有没有更好的办法?【参考方案4】:

关于您的最后一个问题:“现在还重要吗?” 这是一个个案问题。如果 ajax 请求正在做一些不需要安全性的事情(例如加载最新的股票报价),那么恕我直言,这真的无关紧要。如果请求正在加载应该保护的信息(例如返回识别信息或在服务器上执行某些操作),那么您应该这样对待它。

我个人不使用服务器变量来知道什么时候是 ajax 请求。相反,我只是在 ajax 调用中添加一个查询参数(例如http://domain.com/?ajax=true)。如果我需要保护 ajax 调用,那么我将使用与保护常规页面请求相同的方法(同时使用客户端和服务器)。正如卢卡斯·阿曼(Lucas Oman)所指出的,客户端的任何东西都可以伪造。底线不要相信任何请求,即使您认为它来自您的站点或数据库。始终遵循口头禅“过滤输入 - 转义输出”。

【讨论】:

是的,是的,是的。吉姆说得很有道理。听他的。 这是最好的答案。您永远无法确定对您网站的请求的来源。即使请求已通过身份验证,用户也可能使用机器人和有效凭据,甚至只是将他们的 cookie 从浏览器中提取出来。【参考方案5】:

确实,最安全的方法是按照您的建议使用服务器端会话,因为这些会话无法像 cookie 那样制作。

当然,仍然有人可以劫持会话 ID,但是如果您还将用户的 IP 地址存储在他们的会话中并在每次请求时检查它,您就可以清除很多劫持。只有同一 LAN 或代理上的人才能劫持它。

提到的任何其他方法——cookies、javascript、http referer——依赖于客户端数据,这是不安全的,应该始终怀疑是假的、伪造的、劫持的和恶意构建的。

【讨论】:

同意,我们可以改变会话的生成方式,这样会更安全。【参考方案6】:

让你控制

生成访问令牌 存储在会话中以供以后比较

在你看来

将访问令牌声明为JS变量 随每个请求发送令牌

回到你的控制器

验证 HTTP_X_REQUESTED_WITH 验证令牌

检查这些安全guidelines from OpenAjax。 另外,请阅读有关 codinghorror.com Annie 链接的文章。

【讨论】:

+1 由于基于浏览器的 AJAX 请求会随每个请求发送 cookie - 您只需在每个页面上添加令牌检查。 这不会阻止机器人直接访问 API。它可以像你的 JavaScript 应用一样获取访问令牌。【参考方案7】:

大卫沃尔什有一个很好的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.... */

【讨论】:

这与保护请求无关。 任何人都可以在请求中添加 HTTP 标头。【参考方案8】:

检查$_SERVER['HTTP_REFERER']。这在许多情况下都有效,但不应与完全安全的解决方案混淆。

【讨论】:

这会给你你需要的东西,但并不可靠,因为浏览器可以把它喜欢的任何东西放进去。很容易造假,所以价值不能真正被信任。从根本上说,您不能保证请求来自任何地方,即使您向页面发出令牌并让您的 ajax 请求将令牌传回给您。真的,你要问问自己,这有关系吗? @Jonathan Sampson - 我可以在 BurpSuite 之类的东西中捕获请求并更改 HTTP_REFERER ......所以我认为这不是解决方案。我也可以在 w/curl 的脚本中做到这一点。

以上是关于在 PHP 中检测 Ajax 并确保请求来自我自己的网站的主要内容,如果未能解决你的问题,请参考以下文章

如何在php中检测ajax跨域请求

在 PHP 中检测获取请求

检测 AJAX 请求调用的 Iframe 的滚动事件

如何通过php的curl模拟ajax请求,获取其返回值

php [PHP]检测AJAX请求

在 Django Rest Framework 中对来自我的网站的 Ajax 请求进行权限绕过