open-telemetry / open-telemetry/opentelemetry-php

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

Open
#2,028 1 comment 0 reactions 1 assignee View on GitHub

@ChrisLightfootWild is already working on this.

Since Aug 12, 2026.

enhancement opentelemetry-php-contrib
Dominant language
PHP
Stars
912
Forks
232
Avg merge
7d 16h
Merged PRs (30d)
4

Description

What happens

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

Server spanssrc/Hooks/Illuminate/Contracts/Http/Kernel.php:58:

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

Client spanssrc/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

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.