PHP Session 与 AJAX 冲突

发布于 2024-10-14 06:15:09 字数 573 浏览 1 评论 0原文

代码讲一千个字

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 目的

code speaks a thousand words

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

Every thing works fine until the user opens page.php?id=456 on another tab, the ajax returns 'invalid request' on page.php?id=123 How to resolve this conflict?

ps: if its possible i want new session for each page for CSRF purposes

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

再浓的妆也掩不了殇 2024-10-21 06:15:09

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

 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通过会话,站点执行请求,就像 Alice 请求的一样。例如,如果 Alice 的银行是 http://www.mybank.com,那么 Bob 可以创建一个论坛帖子包含

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

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

  1. :进入她的银行网站,以便银行记住她。这可能发生在 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 中。如果他尝试创建一个指向您的某个页面的链接,则该链接要么包含错误的密钥,要么不包含任何密钥,您可以拒绝它。 (公平地说,他有可能正确猜测恰好查看其代码的特定用户的令牌,但他可能更有可能着火。)

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

If I understand this correctly, it sounds like you're setting the token for each request. My guess would be that the old page still has the old token. I would check to see if the token is set before automatically blowing it away.

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

Edit To address your question.

Rather than just using one key of "token," create a key for each browser session.

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

The trick, of course will be figuring out when those happen because if you can't use session, you really won't know whether the request is coming from a new tab or an old one. You could the use the query string to pass this parameter around. Basically all requests would have to have this parameter otherwise you don't associate a session with them.

e.g.

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

Ultimately, the user could monkey with this but I'm not sure there's any way around that.

Edit 2 Ok, here's my thought for how you should do this. Security experts out there, feel free to flame me if I have this wrong, like I said before I'm no expert, but it doesn't look like I'm going to get out of this without suggesting something ;-)

The issue with CSFR is that some malicious user, Bob, could create an element on another site that causes Alice's browser to make a request to some other site and, because Alice had previously logged in and that information is stored either as a cookie or Alice is recognized via session, the site executes the request as if Alice had requested it. For instance, if Alice's bank is http://www.mybank.com, then Bob could create a forum post that contains

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

Alice's bank would recognize her browser as making the request, thinking it's her. There are a couple of key things that have to happen (together, either of these failing will cause the attack to fail) to make this a viable attack (these are the key ones to understanding how to prevent it):

  1. Alice has to have logged into her bank site such that the bank remembers her. This could happen either in a cookie ("remember me") or via session. However, if she closes her browser (ends the session) or clears her cookies, there's no threat because the bank site won't recognize her and will deny the request.
  2. Bob has to be able to supply all of the necessary parameters to the request, otherwise the bank website will deny the request.

In order to provide some notion of "state" on top of a stateless protocol (HTTP), you really can't get around the risk in (1). Unless you get people to always click "log out" or close their window etc, there's nothing you can do get around storing information in the browser or session. However, you can prevent (2) from being a problem. My solution to this (and I'm sure there are tons of others) is to generate a hash, like you're doing and store it in session.

For instance,

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

Then, what you do is append that token to all of your internal links.

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

You NEVER store that token in browser memory: i.e. a cookie. When requests are made, before you do anything, you check the token

//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
 }

The key to this is that all of the links on your site will include the token. However, Bob has no way of knowing what that token is because it's not stored in a cookie on the browser. If he attempts to craft a link to one of your pages, it will either contain the wrong key, or it won't contain any key and you can deny it. (To be fair, there is a chance he could correctly guess the token for a specific user that happens to view his code, but he's probably more likely to burst into flames.)

There's no need to assign a timeout to the token either as the token will be obliterated when the browser is closed and need to be regenerated when the user visits the site.

心是晴朗的。 2024-10-21 06:15:09

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

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

What are you trying to do? Can we see the JavaScript?

It looks like you set the session token to a random hash, and then later you check it. So if the user opens page twice, it gets randomized again and invalidates the previous token. What behaviour are you expecting?

鱼窥荷 2024-10-21 06:15:09

您需要让每个页面启动它自己的令牌,否则会发生此冲突。

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

您具体实现的方式,您可能需要更复杂或更专业的东西,但这应该可以帮助您入门。

You need to have each page start it's own token, otherwise this conflict will occur.

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

and

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

Depending on exactly how you are implementing everything you will probably need something more complicated or specialized, but this should get you started.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文