PHP: 如何刷新 cookie

PHP: how to flush cookies

考虑这个例子

index.php:

<script>
    var first = new XMLHttpRequest();
    var second = new XMLHttpRequest();

    first.open('POST', 'foo.php');
    second.open('POST', 'bar.php');

    first.onreadystatechange = function()
    {
        console.log(document.cookie);
    });

    first.send();
    second.send();
</script>

foo.php:

<?php
    setcookie('foo', 'foo');
    sleep(5);

bar.php:

<?php
    setcookie('foo', 'bar');

有没有办法在两个请求都完成后在 cookie 中创建 foo == 'bar',而不是 foo == 'foo'

编辑:我想我需要详细说明为什么这是个问题。

我正在尝试实施 library 以防止 跨站点请求伪造 在我的网站上。该库要求将唯一令牌与来自客户端的任何 POST 请求一起发送。为此,库将预期的令牌存储在 SESSION 中,然后使用 setcookie 将令牌发送到浏览器。

这对我来说是个问题,因为我使用的是自定义 session 商店,它是 MySQL 数据库中的 table。如果两个 POST 请求在足够短的时间内来自同一个客户端,这将创建一个竞争条件,其中第三个请求的数据库中的预期值始终是最后一个 SENT 请求生成的令牌,但是浏览器使用的令牌来自最后完成的请求。如果请求 1 的完成时间比请求 2 长,则 cookie 和数据库与请求 3 不匹配。

这就是我问这个问题的原因。我知道 PHP 中的 header 会立即向浏览器发送任何 header 信息,而不管输出缓冲如何,cookie 本质上只是 headers。那么,为什么我不能专门使用 cookie 强制执行此行为?

我意识到所有这一切都可以通过不发送如此紧密的多个请求来避免,但我没有能力在任何合理的时间内改变它,并且只是将请求排队等待发送之前的完成将对我的用户体验产生很大的负面影响。

恐怕您最初的问题的答案很简单,是否定的。您对连接没有足够的控制权来强制执行任何操作。当您在编辑中写入时,设置 cookie 是通过 headers 完成的,这是异步发生的。

关于您编辑过的问题:您可以通过为每个客户端存储多个令牌来完成这项工作。也就是说,每次客户端有新的请求进来时,不要删除当前的令牌,而只创建一个新的。客户端不仅应该发送正确的令牌,还应该发送正确的 "connection ID"。此 ID 可以简单地作为来自客户端的请求数的计数器。

或者,您可以选择不为 AJAX 请求刷新令牌,而只为正常页面加载刷新令牌。根据您网站的运作方式,这可能不安全。

首先,用于防止 CSRF 的令牌不必更改每个请求,所以我不确定为什么这是一个问题。只需在登录时生成一个令牌并坚持使用即可。

更重要的是,Cookie 不会阻止 CSRF,因为 Cookie 也会针对伪造的请求发送!这不是 CSRF 预防。您应该将此令牌与 POST 数据一起发送,通常作为表单中的隐藏字段。

此外,PHP 已经默认防止同一会话中的竞争条件。如果一个脚本试图 session_start() 一个已经被另一个脚本使用的会话,它将休眠直到另一个调用结束或调用 session_write_close().