diff --git a/docs/features/storage.md b/docs/features/storage.md index 0be2fba..a6fa140 100644 --- a/docs/features/storage.md +++ b/docs/features/storage.md @@ -109,6 +109,14 @@ $cache = new ApcuCache(); $config = new Config($cache); ``` +### With Custom Namespace + +Every key is stored under a namespace prefix (default `Phirewall:`) so Phirewall's entries do not collide with other code sharing the same APCu segment, and `clear()` deletes only that namespace instead of wiping the whole shared segment: + +```php +$cache = new ApcuCache('myapp:firewall:'); +``` + ### Characteristics - Shared memory within a single PHP-FPM pool @@ -439,7 +447,7 @@ phirewall.track.api-calls. Use `$config->setKeyPrefix('myapp')` to change the prefix and avoid collisions when sharing a cache instance. ::: tip Key segments join with `.` -`CacheKeyGenerator` (and the trusted-bot rDNS cache) join key segments with `.` rather than `:`. PSR-16 reserves `:` for the *cache implementation*, not its callers, so joining with `.` keeps Phirewall's own keys spec-compliant. `RedisCache`'s own namespace prefix (default `Phirewall:`) is the backend's keyspace, applied *after* the public key, and is unaffected. +`CacheKeyGenerator` (and the trusted-bot rDNS cache) join key segments with `.` rather than `:`. PSR-16 reserves `:` for the *cache implementation*, not its callers, so joining with `.` keeps Phirewall's own keys spec-compliant. `RedisCache`'s and `ApcuCache`'s own namespace prefix (default `Phirewall:` for both) is the backend's keyspace, applied *after* the public key, and is unaffected. ::: See [Discriminator Normalizer](/advanced/discriminator-normalizer) for details on how keys are sanitized. diff --git a/docs/getting-started.md b/docs/getting-started.md index ae5bc56..1f92afa 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -900,6 +900,7 @@ new TrustedProxyResolver( - **The default header is a single header.** `allowedHeaders` defaults to `['X-Forwarded-For']` only. If your stack emits the RFC 7239 `Forwarded` header instead, pass it explicitly: `new TrustedProxyResolver([...], ['Forwarded'])`, or `['Forwarded', 'X-Forwarded-For']` for both, so the header the resolver trusts is visible at the call site rather than inferred. - **Proxy headers are read only when the direct peer is trusted.** The resolver consults `X-Forwarded-For` (or `Forwarded`) only when `REMOTE_ADDR`, the address that connected, is itself in your trusted-proxy list. A request arriving directly from an untrusted client has its forwarded headers ignored and is keyed on `REMOTE_ADDR`. - **All header instances are folded into one chain.** Whether intermediaries keep `X-Forwarded-For` as separate lines or fold them into one comma-separated value (the nginx default), the resolver flattens them and walks the chain right to left, returning the first hop that is not in your trusted-proxy list. The protection is this trusted-hop walk, not the number or order of header instances: a client-prepended value sits to the left of the addresses your proxies append, so it is returned only if every hop to its right is trusted. Correct trusted ranges are therefore essential, and stripping or overwriting the inbound header at the edge prevents spoofing outright. +- **An unparsable hop stops the walk.** If a chain entry cannot be parsed as an IP address - the literal `for=unknown`, an RFC 7239 obfuscated identifier (`for=_hidden`), or a malformed value - the walk treats it as terminal and falls back to `REMOTE_ADDR` rather than stepping past it. An unidentifiable hop breaks the verifiable chain, so entries further left cannot be trusted either. - **IPv6 is canonicalized.** An IPv4-mapped IPv6 peer (`::ffff:203.0.113.7`) collapses to its embedded IPv4 form, so a plain IPv4 rule or CIDR matches it and an attacker cannot bypass an IPv4 rule by presenting the mapped form. Alternate *genuine*-IPv6 spellings (expanded `2001:0db8::1` vs compressed `2001:db8::1`, mixed case) are also treated as one identity by `ip()` / CIDR **list** matching, which compares the raw binary address. When a `TrustedProxyResolver` is set via `setIpResolver`, the resolver canonicalizes the address it returns, so per-client keys stay stable regardless of the spelling the client presents; the consistent-spelling caveat applies only to the raw `REMOTE_ADDR` peer address (read via `$request->getServerParams()['REMOTE_ADDR']`) or a custom resolver that does not canonicalize. ## First Test