如果我理解正确,听起来您正在为每个请求设置令牌。我的猜测是旧页面仍然有旧令牌。在自动将其吹走之前,我会检查令牌是否已设置。
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 的银行会识别出是她的浏览器发出请求,并认为是她本人。有几个关键的事情必须发生(这些失败中的任何一个都会导致攻击失败)才能使这种攻击成为可行的攻击(这些是理解如何防止它的关键):
- Alice 必须登录她的银行网站,这样银行才能记住她。这可能发生在 cookie 中(“记住我”)或通过会话发生。但是,如果她关闭浏览器(结束会话)或清除她的 cookie,则不会造成威胁,因为银行网站不会识别她并会拒绝该请求。
- 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 中。如果他试图制作指向您的某个页面的链接,则该链接要么包含错误的密钥,要么不包含任何密钥,您可以拒绝。 (公平地说,他有可能正确猜出碰巧查看他的代码的特定用户的令牌,但他可能更有可能爆发。)
也不需要为令牌分配超时,因为当浏览器关闭时令牌将被删除,并且需要在用户访问网站时重新生成。