0

我正在生成一个唯一令牌并将其保存在每个请求的会话变量中(以典型的 CSRF 保护方式)。令牌在使用 POSTED 令牌值检查验证后刷新。

这是我的代码(index.php):

<?php
    
session_start();

if (!empty($_POST['token'])) {
    var_dump($_POST['token'], $_SESSION['token']);
    exit;
}

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

echo '<form action="index.php" method="post"><input name="token" value="' . $_SESSION['token'] . '"></form>';

当我php -S localhost:8888用来运行脚本时,它工作正常。但是当我指定 index.php 文件时,就像更改php -S localhost:8888 index.php了一样。$_SESSION['token']$_POST['token']并且$_SESSION['token']不匹配)。

php -S localhost:8888

之前 之后

php -S localhost:8888 index.php

之前 之后

我也尝试过使用路由文件。它也不起作用。php -S localhost:8888 server.php

<?php
// server.php

$uri = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH);
$uri = urldecode($uri);

if ($uri !== '/' and file_exists($uri))
{
    return false;
}

require_once 'index.php';

控制台输出:

php -S localhost:8888

php -S localhost:8878 
[Mon Mar 29 11:49:49 2021] PHP 8.0.3 Development Server (http://localhost:8878) started 
[Mon Mar 29 11:49:52 2021] [::1]:47410 Accepted 
[Mon Mar 29 11:49:52 2021] [::1]:47412 Accepted 
[Mon Mar 29 11:49:52 2021] [::1]:47410 [200]: GET / 
[Mon Mar 29 11:49:52 2021] [::1]:47410 Closing 
[Mon Mar 29 11:49:53 2021] [::1]:47412 [404]: GET /favicon.ico - No such file or directory 
[Mon Mar 29 11:49:53 2021] [::1]:47412 Closing

php -S localhost:8888 server.php

php -S localhost:8858 server.php
[Mon Mar 29 11:48:51 2021] PHP 8.0.3 Development Server (http://localhost:8858) started 
[Mon Mar 29 11:48:53 2021] [::1]:33156 Accepted 
[Mon Mar 29 11:48:53 2021] [::1]:33158 Accepted 
[Mon Mar 29 11:48:53 2021] [::1]:33156 Closing 
[Mon Mar 29 11:48:54 2021] [::1]:33158 Closing

测试使用:

PHP 7.3.27-1~deb10u1 (cli) (built: Feb 13 2021 16:31:40) ( NTS )
Copyright (c) 1997-2018 The PHP Group
Zend Engine v3.3.27, Copyright (c) 1998-2018 Zend Technologies
    with Zend OPcache v7.3.27-1~deb10u1, Copyright (c) 1999-2018, by Zend Technologies

PHP 8.0.3 (cli) (built: Mar  5 2021 08:38:30) ( NTS )
Copyright (c) The PHP Group
Zend Engine v4.0.3, Copyright (c) Zend Technologies
    with Zend OPcache v8.0.3, Copyright (c), by Zend Technologies

这是 PHP 内置服务器中的错误吗?

4

1 回答 1

0

路由文件

这是由于您在php: you set the routing file to be index.php 的命令中设置选项的方式...

php -S localhost:8888 index.php
                      ^^^^^^^^^

...这意味着每个请求都将首先通过该文件,然后决定做什么。例如,假设我们的index.php文件包含以下内容:

if (rand(1,100) % 2) {
    echo "Not a multiple of 2\n";
    return true;
} else {
    echo "Multiple of 2\n";
    return false;
}

注意:return true;技术上不需要,但为了清楚起见,我已将其包括在内

这里发生的是发出请求并运行路由文件。如果路由文件返回false(即是2 的倍数),则请求将通过请求的文件。如果代码返回true,则执行停止并且不访问请求的文件。

在您的情况下,请求的文件是index.php(同一个文件),因此,使用上面的代码,您将始终得到如下输出:

Not a multiple of 2

// OR

Multiple of 2
Multiple of 2

// OR 

Multiple of 2
Not a multiple of 2

使用您的代码/设置分解 http 请求

所以,看看你的实际代码......

session_start();

if (!empty($_POST['token'])) {
    var_dump($_POST['token'], $_SESSION['token']);
    exit;
}

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

echo '<form ...>...</form';

这里会发生什么?

  1. 您提出请求index.php*
  2. 服务器访问路由文件index.php来决定做什么
  3. 路由文件输出的数据有效地表明true
  4. 返回路由文件中的数据(表单)并设置 SESSION 变量
    • 文件请求被忽略
  5. 您提交表格
  6. 请求通过路由文件来决定做什么(不是属性的位置action**)
  7. 路由文件检查POST>tokenSESSION>token.
  8. 它们应该匹配,因此路由文件有效地输出指示true***的数据
  9. 返回数据
    • 文件请求(来自action属性)被忽略

* 您尝试访问的文件是 0 差异;尝试一下,infkdsngfdslghfdslgnfdg.php它仍然会以相同的方式工作。您请求的文件永远不会被访问,它似乎只是因为请求的文件和路由的文件是相同的!

** 如上,你可以将 action 属性设置为几乎任何东西,试试fdsfnldgksdf.php

*** 令牌是否匹配,路由文件仍然输出等同于的数据true

根据@brombeer的测试,这确实按预期工作,那么为什么它不适合你呢?

问题

如果您检查服务器正在运行的命令提示符/终端,您会得到正在发生的事情的流(例如,当请求被接受时等)。您会注意到,如果您在发出请求时看到它,您会得到如下结果:

[DATE] [::1]:XXX01 Accepted
[DATE] [::1]:XXX02 Accepted
[DATE] [::1]:XXX01 Closing
[DATE] [::1]:XXX02 Closing

另一方面, @brombeer会得到如下结果:

[DATE] [::1]:XXX01 Accepted
[DATE] [::1]:XXX01 Closing

这就是问题。您正在向@brombeer的一个请求发出两个请求,并且两个请求都通过路由文件。

第一个请求是您期望的请求,并且您会得到预期的输出。但是,在您收到该输出后,第二个请求运行(遵循与上述完全相同的流程 - 请记住,请求什么文件并不重要,脚本将输出相同的内容(!) - 并有效地$_SESSION["token"]将随机数。

如果您更改,这可能会更容易看到...

$_SESSION["token"] = rand();

...至...

$_SESSION["token"]++;

第二个要求是什么?

这与 PHP 无关;这完全与您的浏览器有关。浏览器对所请求文件以外的各种事物提出请求。例如:

  • JavaScript 文件
  • CSS 文件
  • 页面上使用的图像

当然,在这个例子中你没有使用任何一个。但是,无论您是否告诉它们,浏览器都会寻找一些资源:通常基于上下文。

在这种情况下,您的浏览器足够聪明,可以知道您正在尝试访问一个网站(可能是因为端口号,但也可能是因为请求方法或 URI)。

因此,它会尝试找到一些希望在网站上找到的其他文件,特别是:(favicon.ico您应该能够在 Bowser 的开发工具中看到此请求Network)。

index.php正如已经解释的那样,因为您已经使用路由文件设置了服务器,所以请求与or请求经历相同的过程fnjksgjfndsglkjnsf.php。事实上,甚至从未寻找过实际的图标文件。

您可以通过将此代码添加到文件顶部来进一步证明这一点...

if (!strpos($_SERVER["REQUEST_URI"], ".php")) {
    return false;
}

如果请求的文件不是文件,这将停止在您的路由文件中执行php。另外因为我们返回false服务器会寻找图标文件。返回true也可以,但不会查找图标文件。

您也可以尝试将端口更改为类似的东西:8030,我希望代码会按您的预期工作(因为浏览器不会请求网站图标)。

解决方案

回到我对这个问题的第一条评论......

在这种情况下,为什么要将文件传递给服务器?

我仍然不确定您为什么这样做:我认为这是因为您不了解路由文件的作用?或者你不明白你正在创建一个路由文件?

希望我们已经在这里清除了?

无论哪种方式,我都相当确定设置 arouting file不是您想要的,并且出于您的目的,它似乎也不是您需要的。

所以只是不要添加index.php到命令的末尾。


其他工作示例

将您的替换index.php为以下代码:

session_start();

echo "<pre>";

if (!empty($_POST['token'])) {
    var_dump($_POST['token'], $_SESSION['token']);
    exit;
}

$_SESSION['token']++;

echo '<form action="indasdasfdex.php" method="post"><input name="token" value="' . $_SESSION['token'] . '"><input type="submit" value="submit"></form>';

var_dump($_POST['token'] ?? null, $_SESSION['token'] ?? null);

使用作为路由器文件运行您的服务器index.php(根据您的原始问题):

php -S localhost:8888 index.php

// Because you require `index.php` in your `server.php`
// this will work the same if you use `server.php` instead

这就是发生的事情

要求:index.php

Router file runs `$_SESSION["token"] == 1`
Router file returns data to browser with a form: `"token" == 1`
Request terminated
Browser shows returned data
    Form: `token.value == 1`
    `var_dump` output
        `$_POST["token"] == null`
        `$_SESSION["token"] == 1`

要求:favicon.ico

Router file runs `$_SESSION["token"] == 2`
Router file returns data to browser with a form `"token" == 2`
Request terminated
Browser doesn't show returned data (but you can see it in dev tools)
    _Not shown because an image isn't returned!_
    _Can be seen in dev tools_
    Form: `token.value == 2`
    `var_dump` output
        `$_POST["token"] == null`
        `$_SESSION["token"] == 2`

请求:提交表格到index.php

Router file runs
    Dumps `$_SERVER["token"]` and `$_POST["token"]` (2,1)
Router file `exit`
Request terminated
Browser shows returned data
    `var_dump` output
        `$_POST["token"] == 1`
        `$_SESSION["token"] == 2`

要求:favicon.ico

Router file runs `$_SESSION["token"] == 3`
Router file returns data to browser with a form `"token" == 3`
Request terminated
Browser doesn't show returned data (but you can see it in dev tools)
    _Not shown because an image isn't returned!_
    _Can be seen in dev tools_
    Form: `token.value == 3`
    `var_dump` output
        `$_POST["token"] == null`
        `$_SESSION["token"] == 3`**
于 2021-03-28T13:59:50.607 回答