I am generating a unique token and saving it in a session variable on every request (in a typical CSRF protection fashion). Token is refreshed after checking it for validation with the POSTED token value.
Here is my code (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>';
When I use php -S localhost:8888
to run the script, it works fine. But when I specify the index.php file like php -S localhost:8888 index.php
the $_SESSION['token']
is changed. ($_POST['token']
and $_SESSION['token']
does not match).
php -S localhost:8888
php -S localhost:8888 index.php
I have also tried using a routing file. It does not work either. 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';
Console output:
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
Tested using:
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
and
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
Is this a bug in PHP built-in server?
Routing files
This is caused because of the way you have your options set in your command to
php: you set the routing file to be
index.php`......this means that every request will go via that file first and then decide what to do. For example, suppose our
index.php
file contains the following:Note:
return true;
technically isn't needed but I've included it for clarityWhat happens here is that the request is made and the routing file is run. If the routing file returns
false
(i.e. is a Multiple of 2) then the request goes through to the requested file. If the code returnstrue
then execution stops and the requested file isn't accessed.In your case the requested file is
index.php
(the same file) so, with the above code, you will always end up with output like:Break down of the http request with your code/setup
So, looking at your actual code...
What happens here?
index.php
*index.php
to decide what to dotrue
action
**)POST>token
againstSESSION>token
.true
***action
attribute) is ignored* It makes 0 difference what file you attempt to access; try it with
infkdsngfdslghfdslgnfdg.php
and it'll still work the same way. Your requested file is never accessed it only appears that it is because the requested and routed file are the same!** As above, you can set the action attribute to almost anything, try
fdsfnldgksdf.php
*** Whether the tokens match or not the routing file still outputs data which equates to
true
As per @brombeer's test this does work as expected, so why doesn't it work for you?
The problem
If you check the command prompt/terminal where the server is running you get a stream of what is happening (e.g. when a request is a accepted etc.). You'll notice that if you watch that when making a request you get results like:
@brombeer on the other hand will get results like:
This is the problem. You're making two requests to @brombeer's one request and both requests go through the routing file.
The first request is the one you expect and you get output as you would expect. However, after you receive that output the second request runs (which follows the exact same flow as described above - remember it doesn't matter what file is requested the script will output the same thing(!) - and effectively changes the
$_SESSION["token"]
to a new random number.This can perhaps be seen more easily if you change...
...to...
What is the second request?
This is nothing to do with PHP; it's all to do with your browser. Browsers make requests for all sorts of things other than the requested file. For example:
Of course, you aren't using any of that in this example. However, there are some resources that browsers look for whether you tell them to or not: usually based on context.
In this case your browser is smart enough to know that you're trying to access a website (likely because of the port number but maybe because of the request method or URI).
So it tries to locate some additional files that it would expect to find on a website, specifically:
favicon.ico
(you should be able to see this request in your bowser's dev tools underNetwork
).As already explained, because you've set your server up with a routing file that request goes through the same exact process as the
index.php
orfnjksgjfndsglkjnsf.php
requests. In fact the actual icon file is never even looked for.You can prove this further by adding this code to the top of your file...
This will stop execution in your routing file if the requested file isn't a
php
file. Additionally because we returnfalse
the server will look for the icon file. Returningtrue
would also work but the icon file wouldn't be looked for.You could also try changing the port to something like
:8030
and I expect the code would work as you expect (because the browser won't request a favicon).The solution
Referencing back to my first comment on the question...
I'm still not sure why you've done it: I assume that it's because you didn't understand what a routing file did? Or perhaps that you didn't understand that you were creating a routing file?
Hopefully we've cleared that up here?
Either way I am fairly certain that setting up a
routing file
isn't what you intended and for your purposes it doesn't appear that it's what you need either.So just don't add
index.php
to the end of the command.Additional worked example
Replace your
index.php
with the following code:Run your server with
index.php
as the router file (as per your original question):This is what happens
Request:
index.php
Request:
favicon.ico
Request: submit form to
index.php
Request:
favicon.ico