From f25853cf59f285d700ded4b893866f7476701e0b Mon Sep 17 00:00:00 2001 From: Steve Boyd Date: Tue, 17 Feb 2026 13:50:56 +1300 Subject: [PATCH] DOC Document rate limit middleware bypass --- .../09_Security/06_Rate_Limiting.md | 23 +++++++++++++++++++ en/08_Changelogs/6.2.0.md | 19 +++++++++++++++ 2 files changed, 42 insertions(+) diff --git a/en/02_Developer_Guides/09_Security/06_Rate_Limiting.md b/en/02_Developer_Guides/09_Security/06_Rate_Limiting.md index eb8f4fe51..c52e8eed2 100644 --- a/en/02_Developer_Guides/09_Security/06_Rate_Limiting.md +++ b/en/02_Developer_Guides/09_Security/06_Rate_Limiting.md @@ -70,6 +70,29 @@ SilverStripe\Core\Injector\Injector: SiteWideRateLimitMiddleware: '%$SiteWideRateLimitMiddleware' ``` +## Excluding specific URLs from rate limiting + +You can exclude specific endpoints from rate limiting by providing URL regex patterns. This is useful for low-risk endpoints +that should not be subject to rate limiting restrictions. + +Configure excluded patterns by adding `ExcludedURLPatterns` to your middleware configuration: + +```yml +SilverStripe\Core\Injector\Injector: + MyRateLimitMiddleware: + class: SilverStripe\Control\Middleware\RateLimitMiddleware + properties: + # ... + ExcludedURLPatterns: + # Assuming $MyController is handling the 'my-controller' route, + # the following patterns would exclude specific endpoints from rate limiting: + - '#^my-controller/path/to/endpoint$#' + - '#^my-controller/another/endpoint.*#' +``` + +Patterns are matched as regex against the request URL path allowing you to exclude endpoints that provide information +without performing sensitive operations or exposing vulnerabilities (e.g: password strength checks, status endpoints, etc). + ## Disabling the rate limiter You may already solve the rate limiting problem on a server level and the built in rate limiting may well be redundant. diff --git a/en/08_Changelogs/6.2.0.md b/en/08_Changelogs/6.2.0.md index 72786154d..33ae5649a 100644 --- a/en/08_Changelogs/6.2.0.md +++ b/en/08_Changelogs/6.2.0.md @@ -222,6 +222,25 @@ We encourage everyone to remove the `<% base_tag %>` directive from their templa > [!NOTE] > When [`SSViewer.enable_base_tag`](api:SilverStripe\View\SSViewer->enable_base_tag) is set to false, the [`AdminRootController::admin_url()`](api:SilverStripe\Admin\AdminRootController::admin_url()) method now returns URLs of the form `/admin/pages` (or `/mysite/admin/pages` if you site runs in a subfolder) instead of `admin/pages`. If you call this method directly, it might pay to make sure your logic can handle this scenario (e.g. if doing string-matching on the value). +### Rate-limited password strength checking no longer blocks password changes {#rate-limit-strength-exclusion} + +The password strength meter on the change password form fires AJAX requests on every keystroke to provide real-time feedback to users. With the default Security controller rate limit of 10 requests per minute, users found themselves blocked with HTTP 429 ("Too Many Requests") after typing just 10 characters, making the form unusable until the rate limit reset. + +We've resolved this by adding URL pattern exclusion support to `RateLimitMiddleware`. This allows specific low-risk endpoints to bypass rate limiting entirely. The password strength endpoint is now excluded from rate limiting by default, since it only returns an entropy score and does not expose sensitive data or perform authentication-sensitive operations. + +If you've configured custom rate limiting and need to exclude additional endpoints, you can do so by setting `ExcludedURLPatterns` in your middleware configuration: + +```yml +SilverStripe\Core\Injector\Injector: + SecurityRateLimitMiddleware: + properties: + ExcludedURLPatterns: + - '#^path/to/endpoint$#' + - '#^another/endpoint.*#' +``` + +Patterns are matched as regex against the request URL path and are validated at configuration time to catch invalid patterns early. + ### Other new features and enhancements {#other-new} - A new [`ChildFieldManager`](api:SilverStripe\Forms\ChildFieldManager) interface has been added to allow a parent form field to strictly control its children, but still allow setting/getting values for those fields let them handle AJAX requests. See [`ChildFieldManager` docs](/developer_guides/forms/field_types/childfieldmanager/) for more details.