From ca56e8d78e468874b9267c94d8ec75240b6da0ab Mon Sep 17 00:00:00 2001 From: Serge Latyntcev Date: Wed, 27 Feb 2019 14:50:49 +1300 Subject: [PATCH] [CVE-2019-12246] Denial of Service on flush and development URL tools --- _config/confirmation-middleware.yml | 35 ++ _config/dev.yml | 2 + _config/requestprocessors.yml | 94 ++++ .../03_Environment_Management.md | 7 +- .../02_Controllers/05_Middlewares.md | 3 +- .../02_Controllers/06_Builtin_Middlewares.md | 21 + .../How_Tos/Extend_CMS_Interface.md | 18 +- .../16_Execution_Pipeline/01_Flushable.md | 20 +- docs/en/02_Developer_Guides/17_CLI/index.md | 27 +- docs/en/04_Changelogs/4.4.0.md | 131 ++++++ src/Control/Director.php | 56 ++- src/Control/HTTPApplication.php | 74 ++- .../Middleware/CanonicalURLMiddleware.php | 6 +- .../Middleware/ChangeDetectionMiddleware.php | 2 +- .../Middleware/ConfirmationMiddleware.php | 295 ++++++++++++ .../ConfirmationMiddleware/AjaxBypass.php | 25 + .../ConfirmationMiddleware/Bypass.php | 22 + .../ConfirmationMiddleware/CliBypass.php | 25 + .../EnvironmentBypass.php | 68 +++ .../ConfirmationMiddleware/GetParameter.php | 112 +++++ .../HttpMethodBypass.php | 81 ++++ .../ConfirmationMiddleware/PathAware.php | 53 +++ .../ConfirmationMiddleware/Rule.php | 22 + .../Middleware/ConfirmationMiddleware/Url.php | 197 ++++++++ .../UrlPathStartswith.php | 83 ++++ .../UrlPathStartswithCaseInsensitive.php | 20 + .../Middleware/ExecMetricMiddleware.php | 70 +++ src/Control/Middleware/FlushMiddleware.php | 6 +- .../PermissionAwareConfirmationMiddleware.php | 159 +++++++ .../Middleware/URLSpecialsMiddleware.php | 78 +++ .../URLSpecialsMiddleware/FlushScheduler.php | 42 ++ .../SessionEnvTypeSwitcher.php | 52 ++ src/Core/CoreKernel.php | 68 ++- src/Core/Environment.php | 10 + src/Core/Manifest/ClassManifest.php | 86 +++- .../Startup/AbstractConfirmationToken.php | 2 + src/Core/Startup/CallbackFlushDiscoverer.php | 31 ++ src/Core/Startup/CompositeFlushDiscoverer.php | 22 + src/Core/Startup/ConfirmationTokenChain.php | 2 + src/Core/Startup/DeployFlushDiscoverer.php | 120 +++++ src/Core/Startup/ErrorControlChain.php | 2 + .../Startup/ErrorControlChainMiddleware.php | 21 +- src/Core/Startup/ErrorDirector.php | 2 + src/Core/Startup/FlushDiscoverer.php | 20 + .../Startup/ParameterConfirmationToken.php | 8 +- src/Core/Startup/RequestFlushDiscoverer.php | 88 ++++ src/Core/Startup/ScheduledFlushDiscoverer.php | 66 +++ src/Core/Startup/URLConfirmationToken.php | 2 + src/Dev/DevConfirmationController.php | 33 ++ src/Dev/DevelopmentAdmin.php | 12 + src/Dev/Install/Installer.php | 19 +- src/Security/Confirmation/Form.php | 171 +++++++ src/Security/Confirmation/Handler.php | 87 ++++ src/Security/Confirmation/Item.php | 103 ++++ src/Security/Confirmation/Storage.php | 444 ++++++++++++++++++ src/Security/Security.php | 5 +- tests/php/Control/DirectorTest.php | 32 +- tests/php/Control/FlushMiddlewareTest.php | 9 +- tests/php/Control/HttpRequestMockBuilder.php | 57 +++ .../ConfirmationMiddleware/AjaxBypassTest.php | 24 + .../GetParameterTest.php | 46 ++ .../HttpMethodBypassTest.php | 32 ++ .../UrlPathStartswithCaseInsensitiveTest.php | 80 ++++ .../UrlPathStartswithTest.php | 80 ++++ .../ConfirmationMiddleware/UrlTest.php | 107 +++++ .../Middleware/ConfirmationMiddlewareTest.php | 82 ++++ .../Startup/ConfirmationTokenChainTest.php | 187 -------- .../ErrorControlChainMiddlewareTest.php | 176 ------- .../BlankKernel.php | 18 - .../Core/Startup/ErrorControlChainTest.php | 325 ------------- .../ErrorControlChainTest_Chain.php | 96 ---- .../ParameterConfirmationTokenTest.php | 170 ------- .../ParameterConfirmationTokenTest_Token.php | 23 - ...ameterConfirmationTokenTest_ValidToken.php | 15 - .../Core/Startup/URLConfirmationTokenTest.php | 148 ------ .../URLConfirmationTokenTest/StubToken.php | 27 -- .../StubValidToken.php | 15 - .../php/Security/Confirmation/StorageTest.php | 157 +++++++ tests/php/View/SSViewerCacheBlockTest.php | 2 + 79 files changed, 3940 insertions(+), 1298 deletions(-) create mode 100644 _config/confirmation-middleware.yml create mode 100644 docs/en/02_Developer_Guides/02_Controllers/06_Builtin_Middlewares.md create mode 100644 docs/en/04_Changelogs/4.4.0.md create mode 100644 src/Control/Middleware/ConfirmationMiddleware.php create mode 100644 src/Control/Middleware/ConfirmationMiddleware/AjaxBypass.php create mode 100644 src/Control/Middleware/ConfirmationMiddleware/Bypass.php create mode 100644 src/Control/Middleware/ConfirmationMiddleware/CliBypass.php create mode 100644 src/Control/Middleware/ConfirmationMiddleware/EnvironmentBypass.php create mode 100644 src/Control/Middleware/ConfirmationMiddleware/GetParameter.php create mode 100644 src/Control/Middleware/ConfirmationMiddleware/HttpMethodBypass.php create mode 100644 src/Control/Middleware/ConfirmationMiddleware/PathAware.php create mode 100644 src/Control/Middleware/ConfirmationMiddleware/Rule.php create mode 100644 src/Control/Middleware/ConfirmationMiddleware/Url.php create mode 100644 src/Control/Middleware/ConfirmationMiddleware/UrlPathStartswith.php create mode 100644 src/Control/Middleware/ConfirmationMiddleware/UrlPathStartswithCaseInsensitive.php create mode 100644 src/Control/Middleware/ExecMetricMiddleware.php create mode 100644 src/Control/Middleware/PermissionAwareConfirmationMiddleware.php create mode 100644 src/Control/Middleware/URLSpecialsMiddleware.php create mode 100644 src/Control/Middleware/URLSpecialsMiddleware/FlushScheduler.php create mode 100644 src/Control/Middleware/URLSpecialsMiddleware/SessionEnvTypeSwitcher.php create mode 100644 src/Core/Startup/CallbackFlushDiscoverer.php create mode 100644 src/Core/Startup/CompositeFlushDiscoverer.php create mode 100644 src/Core/Startup/DeployFlushDiscoverer.php create mode 100644 src/Core/Startup/FlushDiscoverer.php create mode 100644 src/Core/Startup/RequestFlushDiscoverer.php create mode 100644 src/Core/Startup/ScheduledFlushDiscoverer.php create mode 100644 src/Dev/DevConfirmationController.php create mode 100644 src/Security/Confirmation/Form.php create mode 100644 src/Security/Confirmation/Handler.php create mode 100644 src/Security/Confirmation/Item.php create mode 100644 src/Security/Confirmation/Storage.php create mode 100644 tests/php/Control/HttpRequestMockBuilder.php create mode 100644 tests/php/Control/Middleware/ConfirmationMiddleware/AjaxBypassTest.php create mode 100644 tests/php/Control/Middleware/ConfirmationMiddleware/GetParameterTest.php create mode 100644 tests/php/Control/Middleware/ConfirmationMiddleware/HttpMethodBypassTest.php create mode 100644 tests/php/Control/Middleware/ConfirmationMiddleware/UrlPathStartswithCaseInsensitiveTest.php create mode 100644 tests/php/Control/Middleware/ConfirmationMiddleware/UrlPathStartswithTest.php create mode 100644 tests/php/Control/Middleware/ConfirmationMiddleware/UrlTest.php create mode 100644 tests/php/Control/Middleware/ConfirmationMiddlewareTest.php delete mode 100644 tests/php/Core/Startup/ConfirmationTokenChainTest.php delete mode 100644 tests/php/Core/Startup/ErrorControlChainMiddlewareTest.php delete mode 100644 tests/php/Core/Startup/ErrorControlChainMiddlewareTest/BlankKernel.php delete mode 100644 tests/php/Core/Startup/ErrorControlChainTest.php delete mode 100644 tests/php/Core/Startup/ErrorControlChainTest/ErrorControlChainTest_Chain.php delete mode 100644 tests/php/Core/Startup/ParameterConfirmationTokenTest.php delete mode 100644 tests/php/Core/Startup/ParameterConfirmationTokenTest/ParameterConfirmationTokenTest_Token.php delete mode 100644 tests/php/Core/Startup/ParameterConfirmationTokenTest/ParameterConfirmationTokenTest_ValidToken.php delete mode 100644 tests/php/Core/Startup/URLConfirmationTokenTest.php delete mode 100644 tests/php/Core/Startup/URLConfirmationTokenTest/StubToken.php delete mode 100644 tests/php/Core/Startup/URLConfirmationTokenTest/StubValidToken.php create mode 100644 tests/php/Security/Confirmation/StorageTest.php diff --git a/_config/confirmation-middleware.yml b/_config/confirmation-middleware.yml new file mode 100644 index 000000000..70089ed4d --- /dev/null +++ b/_config/confirmation-middleware.yml @@ -0,0 +1,35 @@ +--- +Name: confirmation_middleware-prototypes +--- +SilverStripe\Core\Injector\Injector: + SilverStripe\Control\Middleware\ConfirmationMiddleware\AjaxBypass: + class: SilverStripe\Control\Middleware\ConfirmationMiddleware\AjaxBypass + type: prototype + + SilverStripe\Control\Middleware\ConfirmationMiddleware\GetParameter: + class: SilverStripe\Control\Middleware\ConfirmationMiddleware\GetParameter + type: prototype + + SilverStripe\Control\Middleware\ConfirmationMiddleware\UrlPathStartswith: + class: SilverStripe\Control\Middleware\ConfirmationMiddleware\UrlPathStartswith + type: prototype + + SilverStripe\Control\Middleware\ConfirmationMiddleware\UrlPathStartswithCaseInsensitive: + class: SilverStripe\Control\Middleware\ConfirmationMiddleware\UrlPathStartswithCaseInsensitive + type: prototype + + SilverStripe\Control\Middleware\ConfirmationMiddleware\EnvironmentBypass: + class: SilverStripe\Control\Middleware\ConfirmationMiddleware\EnvironmentBypass + type: prototype + + SilverStripe\Control\Middleware\ConfirmationMiddleware\CliBypass: + class: SilverStripe\Control\Middleware\ConfirmationMiddleware\CliBypass + type: prototype + + SilverStripe\Control\Middleware\ConfirmationMiddleware\HttpMethodBypass: + class: SilverStripe\Control\Middleware\ConfirmationMiddleware\HttpMethodBypass + type: prototype + + SilverStripe\Control\Middleware\ConfirmationMiddleware\Url: + class: SilverStripe\Control\Middleware\ConfirmationMiddleware\Url + type: prototype \ No newline at end of file diff --git a/_config/dev.yml b/_config/dev.yml index dec3c14ae..c884d4ec3 100644 --- a/_config/dev.yml +++ b/_config/dev.yml @@ -11,3 +11,5 @@ SilverStripe\Dev\DevelopmentAdmin: controller: SilverStripe\Dev\TaskRunner links: tasks: 'See a list of build tasks to run' + confirm: + controller: SilverStripe\Dev\DevConfirmationController diff --git a/_config/requestprocessors.yml b/_config/requestprocessors.yml index 13cb7cdf6..777fba21e 100644 --- a/_config/requestprocessors.yml +++ b/_config/requestprocessors.yml @@ -32,6 +32,7 @@ SilverStripe\Core\Injector\Injector: RequestHandler: '%$SilverStripe\Security\Security' Middlewares: - '%$SecurityRateLimitMiddleware' + --- Name: errorrequestprocessors After: @@ -40,6 +41,8 @@ After: SilverStripe\Core\Injector\Injector: # Note: If Director config changes, take note it will affect this config too SilverStripe\Core\Startup\ErrorDirector: '%$SilverStripe\Control\Director' + + --- Name: canonicalurls --- @@ -48,3 +51,94 @@ SilverStripe\Core\Injector\Injector: properties: ForceSSL: false ForceWWW: false + + +--- +Name: url_specials-middleware +After: + - 'requestprocessors' + - 'coresecurity' +--- +SilverStripe\Core\Injector\Injector: + SilverStripe\Control\Director: + properties: + Middlewares: + URLSpecialsMiddleware: '%$SilverStripe\Control\Middleware\URLSpecialsMiddleware' + + SilverStripe\Control\Middleware\URLSpecialsMiddleware: + class: SilverStripe\Control\Middleware\URLSpecialsMiddleware + properties: + ConfirmationStorageId: 'url-specials' + ConfirmationFormUrl: '/dev/confirm' + Bypasses: + - '%$SilverStripe\Control\Middleware\ConfirmationMiddleware\CliBypass' + - '%$SilverStripe\Control\Middleware\ConfirmationMiddleware\EnvironmentBypass("dev")' + - '%$SilverStripe\Control\Middleware\ConfirmationMiddleware\UrlPathStartswith("dev/confirm")' + EnforceAuthentication: true + AffectedPermissions: + - ADMIN + + +--- +Name: dev_urls-confirmation-middleware +After: + - 'url_specials-middleware' +--- +# This middleware enforces confirmation (CSRF protection) for all URLs +# that start with "dev/*", with the exception for "dev/build" which is handled +# by url_specials-middleware + +# If you want to make exceptions for some URLs, +# see "dev_urls-confirmation-exceptions" config + +SilverStripe\Core\Injector\Injector: + SilverStripe\Control\Director: + properties: + Middlewares: + DevUrlsConfirmationMiddleware: '%$DevUrlsConfirmationMiddleware' + + DevUrlsConfirmationMiddleware: + class: SilverStripe\Control\Middleware\PermissionAwareConfirmationMiddleware + constructor: + - '%$SilverStripe\Control\Middleware\ConfirmationMiddleware\UrlPathStartswith("dev")' + properties: + ConfirmationStorageId: 'dev-urls' + ConfirmationFormUrl: '/dev/confirm' + Bypasses: + - '%$SilverStripe\Control\Middleware\ConfirmationMiddleware\CliBypass' + - '%$SilverStripe\Control\Middleware\ConfirmationMiddleware\EnvironmentBypass("dev")' + EnforceAuthentication: false + AffectedPermissions: + - ADMIN + +--- +Name: dev_urls-confirmation-exceptions +After: + - 'dev_urls-confirmation-middleware' +--- +# This config is the place to add custom bypasses for modules providing UIs +# on top of DevelopmentAdmin (dev/*) + +# If the module has its own CSRF protection, the easiest way would be to +# simply add UrlPathStartswith with the path to the mount point. +# Example: +# # This will prevent confirmation for all URLs starting with "dev/custom-module-endpoint/" +# # WARNING: this won't prevent confirmation for "dev/custom-module-endpoint-suffix/" +# - '%$SilverStripe\Control\Middleware\ConfirmationMiddleware\UrlPathStartswith("dev/custom-module-endpoint")' + +# If the module does not implement its own CSRF protection but exposes all +# dangerous effects through POST, then you could simply exclude GET and HEAD requests +# by using HttpMethodBypass("GET", "HEAD"). In that case GET/HEAD requests will not +# trigger confirmation redirects. +SilverStripe\Core\Injector\Injector: + DevUrlsConfirmationMiddleware: + properties: + Bypasses: + # dev/build is covered by URLSpecialsMiddleware + - '%$SilverStripe\Control\Middleware\ConfirmationMiddleware\UrlPathStartswith("dev/build")' + + # The confirmation form is where people will be redirected for confirmation. We don't want to block it. + - '%$SilverStripe\Control\Middleware\ConfirmationMiddleware\UrlPathStartswith("dev/confirm")' + + # Allows GET requests to the dev index page + - '%$SilverStripe\Control\Middleware\ConfirmationMiddleware\Url("dev", ["GET", "HEAD"])' diff --git a/docs/en/00_Getting_Started/03_Environment_Management.md b/docs/en/00_Getting_Started/03_Environment_Management.md index e1e98c5a6..2171b1589 100644 --- a/docs/en/00_Getting_Started/03_Environment_Management.md +++ b/docs/en/00_Getting_Started/03_Environment_Management.md @@ -1,7 +1,7 @@ # Environment management As part of website development and hosting it is natural for our sites to be hosted on several different environments. -These can be our laptops for local development, a testing server for customers to test changes on, or a production +These can be our laptops for local development, a testing server for customers to test changes on, or a production server. For each of these environments we may require slightly different configurations for our servers. This could be our debug @@ -12,7 +12,7 @@ provides a set of APIs and helpers. ## Security considerations -Sensitive credentials should not be stored in a VCS or project code and should only be stored on the environment in +Sensitive credentials should not be stored in a VCS or project code and should only be stored on the environment in question. When using live environments the use of `.env` files is discouraged and instead one should use "first class" environment variables. @@ -29,7 +29,7 @@ set. An example `.env` file is included in the default installer named `.env.exa ## Managing environment variables with Apache -You can set "real" environment variables using Apache. Please +You can set "real" environment variables using Apache. Please [see the Apache docs for more information](https://httpd.apache.org/docs/current/env.html) ## How to access the environment variables @@ -98,3 +98,4 @@ SilverStripe core environment variables are listed here, though you're free to d | `SS_DATABASE_SSL_CERT` | Absolute path to SSL certificate file | | `SS_DATABASE_SSL_CA` | Absolute path to SSL Certificate Authority bundle file | | `SS_DATABASE_SSL_CIPHER` | Optional setting for custom SSL cipher | +| `SS_FLUSH_ON_DEPLOY` | Try to detect deployments through file system modifications and flush on the first request after every deploy. Does not run "dev/build", but only "flush". Possible values are `true` (check for a framework PHP file modification time), `false` (no checks, skip deploy detection) or a path to a specific file or folder to be checked. See [DeployFlushDiscoverer](api:SilverStripe\Core\Startup\DeployFlushDiscoverer) for more details.

False by default. | diff --git a/docs/en/02_Developer_Guides/02_Controllers/05_Middlewares.md b/docs/en/02_Developer_Guides/02_Controllers/05_Middlewares.md index ab0ad3366..d5fe6c0e6 100644 --- a/docs/en/02_Developer_Guides/02_Controllers/05_Middlewares.md +++ b/docs/en/02_Developer_Guides/02_Controllers/05_Middlewares.md @@ -33,7 +33,7 @@ class CustomMiddleware implements HTTPMiddleware return new HTTPResponse('You missed the special header', 400); } - // You can modify the request before + // You can modify the request before // For example, this might force JSON responses $request->addHeader('Accept', 'application/json'); @@ -118,4 +118,5 @@ SilverStripe\Control\Director: ## API Documentation +* [Built-in Middleware](./06_Builtin_Middlewares.md) * [HTTPMiddleware](api:SilverStripe\Control\Middleware\HTTPMiddleware) diff --git a/docs/en/02_Developer_Guides/02_Controllers/06_Builtin_Middlewares.md b/docs/en/02_Developer_Guides/02_Controllers/06_Builtin_Middlewares.md new file mode 100644 index 000000000..dcb7f1230 --- /dev/null +++ b/docs/en/02_Developer_Guides/02_Controllers/06_Builtin_Middlewares.md @@ -0,0 +1,21 @@ +title: Built-in Middleware +summary: Middleware components that come with SilverStripe Framework + +# Built-in Middleware + +SilverStripe Framework has a number of Middleware components. +You may find them in the [SilverStripe\Control\Middleware](api:SilverStripe\Control\Middleware) namespace. + +| Name | Description | +| ---- | ----------- | +| [AllowedHostsMiddleware](api:SilverStripe\Control\Middleware\AllowedHostsMiddleware) | Secures requests by only allowing a whitelist of Host values | +| [CanonicalURLMiddleware](api:SilverStripe\Control\Middleware\CanonicalURLMiddleware) | URL normalisation and redirection | +| [ChangeDetectionMiddleware](api:SilverStripe\Control\Middleware\ChangeDetectionMiddleware) | Change detection via Etag / IfModifiedSince headers, conditionally sending a 304 not modified if possible. |\ +| [ConfirmationMiddleware](api:SilverStripe\Control\Middleware\ConfirmationMiddleware) | Checks whether user manual confirmation is required for HTTPRequest | +| [ExecMetricMiddleware](api:SilverStripe\Control\Middleware\ExecMetricMiddleware) | Display execution metrics in DEV mode | +| [FlushMiddleware](api:SilverStripe\Control\Middleware\FlushMiddleware) | Triggers a call to flush() on all [Flushable](api:SilverStripe\Core\Flushable) implementors | +| [HTTPCacheControlMiddleware](api:SilverStripe\Control\Middleware\HTTPCacheControlMiddleware) | Controls HTTP response cache headers | +| [RateLimitMiddleware](api:SilverStripe\Control\Middleware\RateLimitMiddleware) | Access throttling, controls HTTP Retry-After header | +| [SessionMiddleware](api:SilverStripe\Control\Middleware\SessionMiddleware) | PHP Session initialisation | +| [TrustedProxyMiddleware](api:SilverStripe\Control\Middleware\TrustedProxyMiddleware) | Rewrites headers that provide IP and host details from upstream proxies | +| [URLSpecialsMiddleware](api:SilverStripe\Control\Middleware\URLSpecialsMiddleware) | Controls some of the [URL special variables](../../debugging/url_variable_tools) | diff --git a/docs/en/02_Developer_Guides/15_Customising_the_Admin_Interface/How_Tos/Extend_CMS_Interface.md b/docs/en/02_Developer_Guides/15_Customising_the_Admin_Interface/How_Tos/Extend_CMS_Interface.md index 89ff78df7..1090fc25c 100644 --- a/docs/en/02_Developer_Guides/15_Customising_the_Admin_Interface/How_Tos/Extend_CMS_Interface.md +++ b/docs/en/02_Developer_Guides/15_Customising_the_Admin_Interface/How_Tos/Extend_CMS_Interface.md @@ -26,8 +26,8 @@ We can use this to create a different base template with `LeftAndMain.ss` Copy the template markup of the base implementation at `templates/SilverStripe/Admin/Includes/LeftAndMain_Menu.ss` from the `silverstripe/admin` module -into `mysite/templates/Includes/LeftAndMain_Menu.ss`. It will automatically be picked up by -the CMS logic. Add a new section into the `