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

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

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

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

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

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

这些天还重要吗?

【问题讨论】:

    标签: php ajax security


    【解决方案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 的脚本中做到这一点。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2012-07-31
      • 1970-01-01
      • 2018-01-06
      • 2021-06-06
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多