Remove "url" query param reliance, use index.php

See https://github.com/silverstripe/silverstripe-framework/issues/7430
This commit is contained in:
Ingo Schommer 2017-10-03 12:23:17 +01:00 committed by Damian Mooyman
parent cc99b2ec19
commit 4a94dfe55b
9 changed files with 84 additions and 76 deletions

View File

@ -81,20 +81,32 @@ as a standard first point of upgrade.
#### Upgrade your rewrite rules #### Upgrade your rewrite rules
The location of SilverStripe's "entry file" has changed. Your project and server environment will need The location of SilverStripe's "entry file" has changed. Your project and server environment will need
to adjust the path to this file from `framework/main.php` to `vendor/silverstripe/framework/main.php`. to adjust the path to this file from `framework/main.php` to `index.php`.
If you are running Apache, adjust your `.htaccess` file. For other webservers, If you are running Apache, adjust your `.htaccess` file. For other webservers,
please consult the [installation guides](getting_started/installation/). please consult the [installation guides](getting_started/installation/).
For websites served by Apache, here's an adjusted `.htaccess` excerpt: The `index.php` file should already exist in your project,
but needs its content replaced:
```diff ```php
+RewriteCond %{REQUEST_URI} !^/vendor/silverstripe/framework/main\.php <?php
RewriteRule ^vendor(/|$) - [F,L,NC]
# ... use SilverStripe\Control\HTTPApplication;
use SilverStripe\Control\HTTPRequestBuilder;
use SilverStripe\Core\CoreKernel;
use SilverStripe\Core\Startup\ErrorControlChainMiddleware;
-RewriteRule .* framework/main.php?url=%1 [QSA] require __DIR__ . '/vendor/autoload.php';
+RewriteRule .* vendor/silverstripe/framework/main.php?url=%1 [QSA]
// Build request and detect flush
$request = HTTPRequestBuilder::createFromEnvironment();
// Default application
$kernel = new CoreKernel(BASE_PATH);
$app = new HTTPApplication($kernel);
$app->addMiddleware(new ErrorControlChainMiddleware($app));
$response = $app->handle($request);
$response->output();
``` ```
#### Upgrade references to renamed and namespaced classes #### Upgrade references to renamed and namespaced classes

View File

@ -1,18 +0,0 @@
<?php
use SilverStripe\Control\HTTPApplication;
use SilverStripe\Control\HTTPRequestBuilder;
use SilverStripe\Core\CoreKernel;
use SilverStripe\Core\Startup\ErrorControlChainMiddleware;
require __DIR__ . '/src/includes/autoload.php';
// Build request and detect flush
$request = HTTPRequestBuilder::createFromEnvironment();
// Default application
$kernel = new CoreKernel(BASE_PATH);
$app = new HTTPApplication($kernel);
$app->addMiddleware(new ErrorControlChainMiddleware($app));
$response = $app->handle($request);
$response->output();

View File

@ -404,11 +404,7 @@ class HTTPRequest implements ArrayAccess
$url = ($this->getExtension()) ? $this->url . '.' . $this->getExtension() : $this->url; $url = ($this->getExtension()) ? $this->url . '.' . $this->getExtension() : $this->url;
if ($includeGetVars) { if ($includeGetVars) {
// if we don't unset $vars['url'] we end up with /my/url?url=my/url&foo=bar etc
$vars = $this->getVars(); $vars = $this->getVars();
unset($vars['url']);
if (count($vars)) { if (count($vars)) {
$url .= '?' . http_build_query($vars); $url .= '?' . http_build_query($vars);
} }

View File

@ -17,10 +17,16 @@ class HTTPRequestBuilder
{ {
// Clean and update live global variables // Clean and update live global variables
$variables = static::cleanEnvironment(Environment::getVariables()); $variables = static::cleanEnvironment(Environment::getVariables());
Environment::setVariables($variables); // Currently necessary for SSViewer, etc to work
// Health-check prior to creating environment // Health-check prior to creating environment
return static::createFromVariables($variables, @file_get_contents('php://input')); $req = static::createFromVariables($variables, @file_get_contents('php://input'));
// Normalise URL
$variables['_SERVER']['REQUEST_URI'] = $req->getURL();
Environment::setVariables($variables); // Currently necessary for SSViewer, etc to work
return $req;
} }
/** /**
@ -32,9 +38,7 @@ class HTTPRequestBuilder
*/ */
public static function createFromVariables(array $variables, $input) public static function createFromVariables(array $variables, $input)
{ {
// Strip `url` out of querystring $url = self::getRequestUri($variables);
$url = $variables['_GET']['url'];
unset($variables['_GET']['url']);
// Build request // Build request
$request = new HTTPRequest( $request = new HTTPRequest(
@ -100,7 +104,6 @@ class HTTPRequestBuilder
/** /**
* Clean up HTTP global vars for $_GET / $_REQUEST prior to bootstrapping * Clean up HTTP global vars for $_GET / $_REQUEST prior to bootstrapping
* Will also populate the $_GET['url'] var safely
* *
* @param array $variables * @param array $variables
* @return array Cleaned variables * @return array Cleaned variables
@ -117,33 +120,6 @@ class HTTPRequestBuilder
$variables['_SERVER']['REQUEST_METHOD'] = $variables['_SERVER']['X-HTTP-Method-Override']; $variables['_SERVER']['REQUEST_METHOD'] = $variables['_SERVER']['X-HTTP-Method-Override'];
} }
// Prevent injection of url= querystring argument by prioritising any leading url argument
if (isset($variables['_SERVER']['QUERY_STRING']) &&
preg_match('/^(?<url>url=[^&?]*)(?<query>.*[&?]url=.*)$/', $variables['_SERVER']['QUERY_STRING'], $results)
) {
$queryString = $results['query'].'&'.$results['url'];
parse_str($queryString, $variables['_GET']);
}
// Decode url from REQUEST_URI if not passed via $_GET['url']
if (!isset($variables['_GET']['url'])) {
$url = $variables['_SERVER']['REQUEST_URI'];
// Querystring args need to be explicitly parsed
if (strpos($url, '?') !== false) {
list($url, $queryString) = explode('?', $url, 2);
parse_str($queryString);
}
// Ensure $_GET['url'] is set
$variables['_GET']['url'] = urldecode($url);
}
// Remove base folders from the URL if webroot is hosted in a subfolder
if (substr(strtolower($variables['_GET']['url']), 0, strlen(BASE_URL)) === strtolower(BASE_URL)) {
$variables['_GET']['url'] = substr($variables['_GET']['url'], strlen(BASE_URL));
}
// Merge $_FILES into $_POST // Merge $_FILES into $_POST
$variables['_POST'] = array_merge((array)$variables['_POST'], (array)$variables['_FILES']); $variables['_POST'] = array_merge((array)$variables['_POST'], (array)$variables['_FILES']);
@ -156,4 +132,52 @@ class HTTPRequestBuilder
return $variables; return $variables;
} }
/**
* @param array $variables
* @return string
*/
protected static function getRequestUri(array $variables)
{
$server = $variables['_SERVER'];
$ruLen = strlen($server['REQUEST_URI']);
$snLen = strlen($server['SCRIPT_NAME']);
$isIIS = (strpos($server['SERVER_SOFTWARE'], 'Microsoft-IIS') !== false);
// IIS will populate server variables using one of these two ways
if ($isIIS) {
if ($server['REQUEST_URI'] == $server['SCRIPT_NAME']) {
$url = "";
} elseif ($ruLen > $snLen && substr($server['REQUEST_URI'], 0, $snLen + 1) == ($server['SCRIPT_NAME'] . '/')) {
$url = substr($server['REQUEST_URI'], $snLen + 1);
$url = strtok($url, '?');
} else {
$url = $server['REQUEST_URI'];
if ($url[0] == '/') {
$url = substr($url, 1);
}
$url = strtok($url, '?');
}
// Apache will populate the server variables this way
} else {
// Remove query parameters (they're retained separately through $server['_GET']
$url = parse_url($server['REQUEST_URI'], PHP_URL_PATH);
// if ($ruLen > $snLen && substr($server['REQUEST_URI'], 0, $snLen + 1) == ($server['SCRIPT_NAME'] . '/')) {
// $url = substr($server['REQUEST_URI'], $snLen + 1);
// $url = strtok($url, '?');
// } else {
// $url = "";
// }
}
// Remove base folders from the URL if webroot is hosted in a subfolder
if (substr(strtolower($url), 0, strlen(BASE_URL)) === strtolower(BASE_URL)) {
$url = substr($url, strlen(BASE_URL));
}
return $url;
}
} }

View File

@ -442,7 +442,7 @@ class ClassManifest
$finder = new ManifestFileFinder(); $finder = new ManifestFileFinder();
$finder->setOptions(array( $finder->setOptions(array(
'name_regex' => '/^[^_].*\\.php$/', 'name_regex' => '/^[^_].*\\.php$/',
'ignore_files' => array('index.php', 'main.php', 'cli-script.php'), 'ignore_files' => array('index.php', 'cli-script.php'),
'ignore_tests' => !$includeTests, 'ignore_tests' => !$includeTests,
'file_callback' => function ($basename, $pathname, $depth) use ($includeTests, $finder) { 'file_callback' => function ($basename, $pathname, $depth) use ($includeTests, $finder) {
$this->handleFile($basename, $pathname, $includeTests); $this->handleFile($basename, $pathname, $includeTests);

View File

@ -15,7 +15,7 @@ use Exception;
* $chain = new ErrorControlChain(); * $chain = new ErrorControlChain();
* $chain->then($callback1)->then($callback2)->thenIfErrored($callback3)->execute(); * $chain->then($callback1)->then($callback2)->thenIfErrored($callback3)->execute();
* *
* WARNING: This class is experimental and designed specifically for use pre-startup in main.php * WARNING: This class is experimental and designed specifically for use pre-startup.
* It will likely be heavily refactored before the release of 3.2 * It will likely be heavily refactored before the release of 3.2
*/ */
class ErrorControlChain class ErrorControlChain

View File

@ -16,7 +16,7 @@ use SilverStripe\Security\RandomGenerator;
* established, this class takes care of allowing some other code of confirming the parameter, * established, this class takes care of allowing some other code of confirming the parameter,
* by generating a one-time-use token & redirecting with that token included in the redirected URL * by generating a one-time-use token & redirecting with that token included in the redirected URL
* *
* WARNING: This class is experimental and designed specifically for use pre-startup in main.php * WARNING: This class is experimental and designed specifically for use pre-startup.
* It will likely be heavily refactored before the release of 3.2 * It will likely be heavily refactored before the release of 3.2
*/ */
class ParameterConfirmationToken class ParameterConfirmationToken

View File

@ -439,12 +439,8 @@ ErrorDocument 500 /assets/error-500.html
$baseClause $baseClause
$cgiClause $cgiClause
# Deny access to vendor, unless you're requesting main.php
# Not restricting to the start of the path to support RewriteBase
RewriteCond %{REQUEST_URI} !^/vendor/silverstripe/framework/main\.php
RewriteRule ^vendor(/|$) - [F,L,NC]
# Deny access to potentially sensitive files and folders # Deny access to potentially sensitive files and folders
RewriteRule ^vendor(/|$) - [F,L,NC]
RewriteRule ^\.env - [F,L,NC] RewriteRule ^\.env - [F,L,NC]
RewriteRule silverstripe-cache(/|$) - [F,L,NC] RewriteRule silverstripe-cache(/|$) - [F,L,NC]
RewriteRule composer\.(json|lock) - [F,L,NC] RewriteRule composer\.(json|lock) - [F,L,NC]
@ -455,7 +451,7 @@ ErrorDocument 500 /assets/error-500.html
# Try finding framework in the vendor folder first # Try finding framework in the vendor folder first
RewriteCond %{REQUEST_URI} ^(.*)$ RewriteCond %{REQUEST_URI} ^(.*)$
RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule .* vendor/silverstripe/framework/main.php?url=%1 [QSA] RewriteRule .* index.php
</IfModule> </IfModule>
TEXT; TEXT;
@ -510,9 +506,8 @@ TEXT;
<match url="^(.*)$" /> <match url="^(.*)$" />
<conditions> <conditions>
<add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" /> <add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" />
<add input="vendor/silverstripe/framework/main.php" matchType="IsFile" />
</conditions> </conditions>
<action type="Rewrite" url="vendor/silverstripe/framework/main.php?url={R:1}" appendQueryString="true" /> <action type="Rewrite" url="index.php" appendQueryString="true" />
</rule> </rule>
</rules> </rules>
</rewrite> </rewrite>

View File

@ -94,9 +94,8 @@ class DetailedErrorFormatter implements FormatterInterface
$httpRequest = null; $httpRequest = null;
if (isset($_SERVER['REQUEST_URI'])) { if (isset($_SERVER['REQUEST_URI'])) {
$httpRequest = $_SERVER['REQUEST_URI']; $httpRequest = $_SERVER['REQUEST_URI'];
} elseif (isset($_REQUEST['url'])) {
$httpRequest = $_REQUEST['url'];
} }
if (isset($_SERVER['REQUEST_METHOD'])) { if (isset($_SERVER['REQUEST_METHOD'])) {
$httpRequest = $_SERVER['REQUEST_METHOD'] . ' ' . $httpRequest; $httpRequest = $_SERVER['REQUEST_METHOD'] . ' ' . $httpRequest;
} }