From 58a5483e6f08006a27cd1e872480e6e57f40994c Mon Sep 17 00:00:00 2001 From: Sascha Egerer Date: Fri, 29 May 2026 22:20:43 +0200 Subject: [PATCH] Document KeyExtractors::hashedHeader for credential-bearing headers Adds a row for hashedHeader() to the rate-limiting helpers table and switches the api-key examples in common-attacks, examples, and fail2ban to hashedHeader() so the cache backend and ban registry store a sha256 fingerprint rather than the raw secret. --- docs/common-attacks.md | 4 ++-- docs/examples.md | 5 +++-- docs/features/fail2ban.md | 6 ++++-- docs/features/rate-limiting.md | 5 ++++- 4 files changed, 13 insertions(+), 7 deletions(-) diff --git a/docs/common-attacks.md b/docs/common-attacks.md index b83df3b..71b5ded 100644 --- a/docs/common-attacks.md +++ b/docs/common-attacks.md @@ -368,13 +368,13 @@ $config->allow2ban->add('volume-ban', ### API Key Throttling -Rate-limit by API key for authenticated endpoints: +Rate-limit by API key for authenticated endpoints. `hashedHeader()` stores a sha256 fingerprint of the key in the cache backend rather than the raw value: ```php $config->throttles->add('api-key', limit: 1000, period: 60, - key: KeyExtractors::header('X-Api-Key'), + key: KeyExtractors::hashedHeader('X-Api-Key'), ); ``` diff --git a/docs/examples.md b/docs/examples.md index 0de4766..2de271b 100644 --- a/docs/examples.md +++ b/docs/examples.md @@ -1048,12 +1048,13 @@ $config->allow2ban->add('high-volume-ban', key: KeyExtractors::ip() ); -// Ban by API key for authenticated routes +// Ban by API key for authenticated routes. hashedHeader() stores a sha256 +// fingerprint of the key in the ban registry instead of the raw credential. $config->allow2ban->add('api-key-ban', threshold: 1000, period: 60, banSeconds: 300, - key: KeyExtractors::header('X-Api-Key') + key: KeyExtractors::hashedHeader('X-Api-Key') ); ``` diff --git a/docs/features/fail2ban.md b/docs/features/fail2ban.md index 9c719af..c5b04a2 100644 --- a/docs/features/fail2ban.md +++ b/docs/features/fail2ban.md @@ -349,13 +349,15 @@ Ban API keys that exceed expected usage. Unlike rate limiting (which returns 429 ```php use Flowd\Phirewall\KeyExtractors; -// Ban any API key that makes more than 1000 requests in 60 seconds +// Ban any API key that makes more than 1000 requests in 60 seconds. +// hashedHeader() stores the sha256 fingerprint instead of the raw key so the +// ban registry doesn't carry the credential verbatim. $config->allow2ban->add( name: 'api-key-abuse', threshold: 1000, period: 60, banSeconds: 300, // 5 minute ban - key: KeyExtractors::header('X-Api-Key'), + key: KeyExtractors::hashedHeader('X-Api-Key'), ); ``` diff --git a/docs/features/rate-limiting.md b/docs/features/rate-limiting.md index d55c987..4815b4b 100644 --- a/docs/features/rate-limiting.md +++ b/docs/features/rate-limiting.md @@ -216,13 +216,16 @@ Phirewall ships with common key extractors for typical rate limiting scenarios: |--------|-------------|---------| | `KeyExtractors::ip()` | Client IP from `REMOTE_ADDR` | `?string` | | `KeyExtractors::clientIp($resolver)` | Client IP via trusted proxy resolver | `?string` | -| `KeyExtractors::header('X-User-Id')` | Value of a specific header | `?string` | +| `KeyExtractors::header('X-User-Id')` | Raw value of a specific header | `?string` | +| `KeyExtractors::hashedHeader('X-Api-Key')` | sha256 fingerprint of a header value | `?string` | | `KeyExtractors::method()` | HTTP method (uppercase) | `?string` | | `KeyExtractors::path()` | Request path (always returns a value, never skips) | `string` | | `KeyExtractors::userAgent()` | User-Agent header value | `?string` | All extractors except `path()` return `null` when the value is missing or empty, which causes the throttle rule to be skipped for that request. +Prefer `hashedHeader()` over `header()` whenever the header carries a credential (`Authorization`, `Cookie`, `X-Api-Key`, …). The cache backend and ban registry then store the sha256 fingerprint rather than the raw secret, so anyone with read access to the cache cannot recover the credential. + ### Custom Key Extractors Write your own closure for any logic: