【问题标题】:PHP Session conflicts with AJAXPHP 会话与 AJAX 冲突
【发布时间】:2011-01-25 11:57:02
【问题描述】:

代码会说一千个单词

page.php?id=123

<?php
if(is_ajax()){// function that determines whether the request is from ajax (http header stuff)
$_SESSION['token'] = md5(rand());
}
//some ajax request to ajax.php?id=123
?>

ajax.php?id=123

<?php
if($_SESSION['token'] == $_GET['token']){
echo 'Tell me this is for reall';
}else{
echo 'Invalid Request';
}
?>

一切正常,直到用户在另一个选项卡上打开 page.php?id=456,ajax 在 page.php?id=123 上返回“无效请求” > 如何解决这个冲突?

ps:如果可能的话,我希望每个页面都有新的会话用于 CSRF 目的

【问题讨论】:

    标签: php mysql security session csrf


    【解决方案1】:

    如果我理解正确,听起来您正在为每个请求设置令牌。我的猜测是旧页面仍然有旧令牌。在自动将其吹走之前,我会检查令牌是否已设置。

     if (isset($_SESSION['token'])){
        //do nothing
     } else{
       $_SESSION['token'] = md5(rand());
     }
    

    编辑解决您的问题。

    不要只使用一个“令牌”密钥,而是为每个浏览器会话创建一个密钥。

    $_SESSION[$sessionId] = md5(rand());
    

    诀窍当然是弄清楚这些何时发生,因为如果你不能使用会话,你真的不知道请求是来自新标签还是旧标签。您可以使用查询字符串来传递此参数。基本上所有请求都必须具有此参数,否则您不会将会话与它们关联。

    例如

    http://www.yoursite.com/somepage.php?sessionid=<some generated id>
    

    最终,用户可能会对此感到困惑,但我不确定是否有任何解决方法。

    编辑 2 好的,这是我对如何执行此操作的想法。那里的安全专家,如果我有这个错误,请随意抨击我,就像我在我不是专家之前所说的那样,但看起来我不会在没有建议的情况下摆脱这个;-)

    CSFR 的问题在于,一些恶意用户 Bob 可以在另一个站点上创建一个元素,导致 Alice 的浏览器向另一个站点发出请求,并且因为 Alice 之前已经登录并且该信息存储为cookie 或 Alice 通过 session 被识别,站点执行请求,就好像 Alice 请求了它一样。例如,如果 Alice 的银行是 http://www.mybank.com,那么 Bob 可以创建一个论坛帖子,其中包含

    <img srg="http://www.mybank.com/transferfunds.php?amount=1000&receiver=Bob" />
    

    Alice 的银行会识别出是她的浏览器发出请求,并认为是她本人。有几个关键的事情必须发生(这些失败中的任何一个都会导致攻击失败)才能使这种攻击成为可行的攻击(这些是理解如何防止它的关键):

    1. Alice 必须登录她的银行网站,这样银行才能记住她。这可能发生在 cookie 中(“记住我”)或通过会话发生。但是,如果她关闭浏览器(结束会话)或清除她的 cookie,则不会造成威胁,因为银行网站不会识别她并会拒绝该请求。
    2. Bob 必须能够为请求提供所有必要的参数,否则银行网站将拒绝该请求。

    为了在无状态协议 (HTTP) 之上提供一些“状态”概念,您确实无法规避 (1) 中的风险。除非您让人们总是单击“注销”或关闭他们的窗口等,否则您无法在浏览器或会话中存储信息。但是,您可以防止 (2) 成为问题。我对此的解决方案(我相信还有很多其他的)是生成一个哈希,就像你正在做的那样并将它存储在会话中。

    例如,

    $_SESSION['token'] = md5(rand());
    

    然后,您所做的就是将该令牌附加到您的所有内部链接。

    http://www.mysite.com/secure.php?token=giuwnrefviunslfghahgliuwnvwrgbaasd

    从不将该令牌存储在浏览器内存中:即 cookie。发出请求时,在执行任何操作之前,请检查令牌

    //note, you'll want to sanitize user input, I'm just being brief
    if ($_GET['token'] != $_SESSION['token']){
       //User either attempted to enter a link on their own or it's a CSRF attack
       header('HTTP/1.1 403 Forbidden');
     }else{
     //do whatever needs to be done
     }
    

    关键是您网站上的所有链接都将包含该令牌。但是,Bob 无法知道该令牌是什么,因为它没有存储在浏览器的 cookie 中。如果他试图制作指向您的某个页面的链接,则该链接要么包含错误的密钥,要么不包含任何密钥,您可以拒绝。 (公平地说,他有可能正确猜出碰巧查看他的代码的特定用户的令牌,但他可能更有可能爆发。)

    也不需要为令牌分配超时,因为当浏览器关闭时令牌将被删除,并且需要在用户访问网站时重新生成。

    【讨论】:

    • 但我希望每个页面都有新的会话
    • 那么你不能使用“会话”。大多数浏览器在它们的标签之间共享一个会话。您可以做的是创建一个随机键值对,而不仅仅是“令牌”。我会用一个例子来编辑我的帖子。
    • 在用户登录的整个过程中使用一个会话哈希是否足够?会话设置一次并永久使用,直到用户注销
    • 实际上,我认为只要它不存储在客户端的 cookie 中,它可能会起作用。如果它存储在会话中,则用户永远无法访问它。要求所有请求在查询字符串中包含该哈希,我 认为 你应该很好。尽管我不是安全专家,但最好让安全专家参与进来……
    • 可能,但前提是用户打开了一堆选项卡,即使这样它也只会创建一堆会话哈希,而不是会话。选项卡最终仍将共享同一会话。我会阅读 CSRF 以了解是什么让你容易受到它的影响,什么不是。根据我对它的理解,使用存储在会话(而不是 cookie)中并附加到每个 url 的哈希是可行的。请记住,AJAX 并不是唯一容易受到 CSRF 攻击的位置。即使您不支持 AJAX,您仍然容易受到攻击。
    【解决方案2】:

    你想做什么?我们可以看到 JavaScript 吗?

    看起来您将会话令牌设置为随机哈希,然后您检查它。因此,如果用户打开页面两次,它会再次随机化并使之前的令牌无效。您期待什么行为?

    【讨论】:

    • 我希望 page.php?id=456 和 page.php?id=123 都能够在没有任何冲突的情况下执行 ajax 请求
    【解决方案3】:

    你需要让每个页面启动它自己的token,否则会发生这种冲突。

    <?php
    if(is_ajax()){// function that determines whether the request is from ajax (http header stuff)
    $page_id = $_GET['id'];
    $_SESSION['token'][$page_id] = md5(rand());
    }
    //some ajax request to ajax.php?id=123
    ?>
    

    <?php
    $page_id = $_GET['id'];
    if($_SESSION['token'][$page_id] == $_GET['token']){
    echo 'This is for real!';
    }else{
    echo 'Invalid Request';
    }
    ?>
    

    根据您具体如何实现所有内容,您可能需要更复杂或更专业的东西,但这应该可以帮助您入门。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-02-09
      • 1970-01-01
      • 2012-08-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多