Skip to content

Laravel instrumentation: url.full is recorded unredacted, leaking signed-URL signatures #2028

Description

@hnccox

What happens

opentelemetry-auto-laravel records the full request URL, including the query string, with no redaction of sensitive query parameters.

Server spans — src/Hooks/Illuminate/Contracts/Http/Kernel.php:58:

->setAttribute(TraceAttributes::URL_FULL, $request->fullUrl())

Client spans — src/Watchers/ClientRequestWatcher.php:50-55 strips userinfo, then re-appends the query string verbatim:

$parsedUrl = collect(parse_url($request->request->url()) ?: []);
$processedUrl = $parsedUrl->get('scheme', 'http') . '://' . $parsedUrl->get('host') . $parsedUrl->get('path', '');

if ($parsedUrl->has('query')) {
    $processedUrl .= '?' . $parsedUrl->get('query');   // <- unredacted
}

Why this matters for Laravel specifically

Laravel's signed URLs are a core framework feature used for email verification, password-reset style flows and temporary download links. UrlGenerator::signedRoute() / temporarySignedRoute() append the HMAC as a query parameter:

// Illuminate/Routing/UrlGenerator.php:365,373
$parameters = $parameters + ['expires' => $this->availableAt($expiration)];
...
'signature' => hash_hmac('sha256', $this->route($name, $parameters, $absolute), $key),

The name is hardcoded and reserved — Laravel throws InvalidArgumentException if an application tries to use signature or expires as a route parameter (UrlGenerator.php:391,460). There is no configuration option.

So the value that authorises the request is captured in the span, and remains replayable until expires. This affects every Laravel application using this instrumentation, not a particular configuration.

Expected behaviour per the semantic conventions

The requirement is already vendored into this project as PHPDoc on the very constant the instrumentation sets — sem-conv/Attributes/UrlAttributes.php, above const URL_FULL:

Sensitive content provided in url.full SHOULD be scrubbed when instrumentations can identify it.

Query string values for the following keys SHOULD be redacted by default and replaced by the value REDACTED:

  • AWSAccessKeyId
  • Signature
  • sig
  • X-Goog-Signature

When a query string value is redacted, the query string key SHOULD still be preserved, e.g. https://www.example.com/path?color=blue&sig=REDACTED.

The URL attribute registry places this on the instrumentation, and notes instrumentations MAY allow the list to be overridden.

Current state

grep -riE 'scrub|sanitiz|redact' --include='*.php' across open-telemetry/sdk, open-telemetry/api, open-telemetry/sem-conv and opentelemetry-auto-laravel returns matches only inside PHPDoc comments in sem-conv, plus SanitizeCombinedHeadersPropagationGetter in context, which concerns combined header values rather than URLs.

There appears to be no executable query-parameter redaction anywhere in the PHP implementation, and no configuration surface for it. Userinfo is handled only in ClientRequestWatcher, and not on server spans.

Question: is key matching intended to be case-sensitive?

The default list contains Signature (capitalised, from the AWS S3 query-auth docs). Laravel emits lowercase signature. A case-insensitive implementation would cover Laravel out of the box; a case-sensitive one would not.

The specification does not appear to state this. Clarifying it would be valuable, since the difference decides whether the most widely used PHP framework's signed URLs are protected by the default list or need an override.

Reproduction

  1. Laravel 12 app with open-telemetry/opentelemetry-auto-laravel and OTEL_PHP_AUTOLOAD_ENABLED=true
  2. Define a signed route and generate a link with URL::temporarySignedRoute('verify', now()->addMinutes(60), ['id' => 1])
  3. Request the URL and inspect the exported span

url.full contains the complete ?expires=…&signature=….

Suggested fix

  1. Redact the documented default keys in both Kernel.php (server spans) and ClientRequestWatcher.php (client spans), preserving keys and replacing values with REDACTED.
  2. Consider a shared helper in open-telemetry/sdk, since every instrumentation setting url.full or url.query needs identical behaviour.
  3. Expose an override so applications can add framework-specific keys — signature for Laravel, if matching turns out to be case-sensitive.
  4. Clarify case-sensitivity in the specification, or handle it case-insensitively.

Happy to open a PR if the maintainers agree on the approach and where the helper should live.

Environment

open-telemetry/opentelemetry-auto-laravel 1.8.0
open-telemetry/sdk 1.15.0
open-telemetry/api 1.10.0
laravel/framework v12.64.0
PHP 8.4

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

enhancementNew feature or requestopentelemetry-php-contribThis issue is related to the opentelemetry-php-contrib repository

Type

No type

Projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions