v1.78.0 - Alioth
[1.78.0] - 2026-08-12 — Alioth
Theme: credentials in URL paths stop reaching the logs. Redaction has always been
keyed on parameter names, which covers query strings and body fields but not a secret
carried in the path itself. Applications that issue signed payment links, magic links or
one-time download URLs can now register those route templates and have the credential
segment masked in every framework log sink.
Added
-
Configurable sensitive path redaction —
SensitiveParamRedactor::configureSensitivePaths()
andsanitizePath()mask credential-bearing URL path segments. Applications register
route templates under the newlogging.sensitive_pathsconfig key (or the
LOG_SENSITIVE_PATHScomma-separated env var), andFramework::boot()wires them into
the redactor after provider/extension config is merged:// config/logging.php 'sensitive_paths' => ['/checkout/pay/{token}', '/d/{signature}/file'],
A
{name}segment is replaced with[REDACTED]; a bare*segment matches any single
segment and is kept; segments beyond the pattern are preserved. Matching is anchored at
the start of the path and mirrorsRouter::match()'s own normalization — literal
segments compare case-insensitively after percent-decoding, repeated slashes collapse,
and%2Fis treated as a segment boundary — so the spellings that reach a live route
(//checkout/pay/x,/checkout%2Fpay/x) are redacted too, while a%2Finside a
credential cannot split the secret across segments and half-redact it.Templates are written without the deployment's base URL.
Request::getBaseUrl()is
passed alongside everygetRequestUri()value and stripped before matching, so one
template covers both base-URL-mounted and root-mounted deployments.
Security
-
The request logger and the exception handler no longer emit raw path credentials —
Application::handle()logged'uri' => $request->getPathInfo()verbatim at info level
(dev/staging, or any profile withAPP_DEBUGon), andHandler::report()logged the
request URI at error level in every profile, becauseSensitiveParamRedactor::sanitizeUrl()
redacted the query string but reassembled the path untouched. Both now route through
sanitizePath(). -
Every remaining request-path log sink was swept — the request/response logging
middleware's separate rawpathfield,CSRFMiddleware(error level, and the
SecurityExceptiondetails it raises),AuthMiddleware,SecurityHeadersMiddleware,
AdminPermissionMiddleware,VersionManager,FieldSelectionMiddleware,
TracingMiddleware's span attributes andMetricsMiddleware'sendpoint— which is
persisted to the metrics store, not just logged. Auth access logs and the activity
subscriber inherit the fix throughsanitizeUrl(). Rate-limit bucket keys are
deliberately left raw: they are not a log sink, and redacting them would collapse every
credentialed path onto one bucket. -
sanitizeUrl()no longer mis-parses a schemeless//…request URI —parse_url()
read its first segment as a host, carrying the rest of the path past redaction
untouched.Redaction happens at log-emission time only and never mutates the request, so routing,
signature verification and handlers still see the original path.Host obligation: only the application knows which of its routes are credential-bearing,
so nothing is redacted until it registers its patterns — withlogging.sensitive_paths
empty (the framework default) every path is logged exactly as before, byte-identical.
Redaction also covers framework log sinks only: reverse-proxy, web-server and CDN access
logs record the raw request line and remain the operator's responsibility. And redaction
applies to path- and query-shaped values: an exception whose message interpolates
the request URI is still logged verbatim (seedocs/SECURITY_NOTES.md) — put the URI in
the exception context, which is redacted, not in the message.