Repository navigation
HttpRouteFormatter - Log redaction leaks replayable bearer token #7826
Description
Description
When one path segment of a route has a parameter followed by fixed text, such as . in /a/{token}.{format}, route parameter redaction splits the path in the wrong place. Part or all of a sensitive parameter then appears unredacted in another parameter that isn't redacted. In Formatted mode a parameter ends at the first occurrence of the first character of the fixed text after it (src/Libraries/Microsoft.Extensions.Telemetry/Http/HttpRouteFormatter.cs#L83), while in Structured mode every parameter ends at the next / (src/Libraries/Microsoft.Extensions.Telemetry/Http/HttpRouteParser.cs#L72). ASP.NET Core instead matches the whole fixed text from right to left, and both Microsoft.Extensions.Http.Diagnostics and Microsoft.AspNetCore.Diagnostics.Middleware use this shared code
Reproduction Steps
- dotnet new console
- dotnet add package Microsoft.Extensions.Http.Diagnostics , then do the same for Microsoft.Extensions.Compliance.Testing and Microsoft.Extensions.Logging.Console .
- Replace Program.cs with the code below and run dotnet run .
using Microsoft.Extensions.Compliance.Classification;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Http.Diagnostics;
using Microsoft.Extensions.Http.Logging;
using Microsoft.Extensions.Logging;
var services = new ServiceCollection()
.AddLogging(b => b.AddJsonConsole())
.AddFakeRedaction(o => o.RedactionFormat = "[REDACTED]");
foreach (var mode in new[] { OutgoingPathLoggingMode.Formatted, OutgoingPathLoggingMode.Structured })
{
services.AddHttpClient(mode.ToString())
.ConfigurePrimaryHttpMessageHandler(() => new OkHandler())
.AddExtendedHttpClientLogging(o =>
{
o.RequestPathLoggingMode = mode;
o.RouteParameterDataClasses["token"] = new DataClassification("Demo", "Secret");
o.RouteParameterDataClasses["format"] = DataClassification.None;
});
}
using (var provider = services.BuildServiceProvider())
{
var factory = provider.GetRequiredService<IHttpClientFactory>();
await SendAsync(factory.CreateClient("Formatted"), "/a/{token}.{format}", "/a/part1.part2.part3.json");
await SendAsync(factory.CreateClient("Structured"), "/b/{format}.{token}", "/b/json.secret");
}
static async Task SendAsync(HttpClient client, string route, string path)
{
using var request = new HttpRequestMessage(HttpMethod.Get, "http://localhost" + path);
request.SetRequestMetadata(new RequestMetadata { RequestRoute = route });
using var response = await client.SendAsync(request);
}
sealed class OkHandler : HttpMessageHandler
{
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
=> Task.FromResult(new HttpResponseMessage(System.Net.HttpStatusCode.OK));
}Expected behavior
For these routes, ASP.NET Core binds token=part1.part2.part3, format=json for the first request and format=json, token=secret for the second. The whole token value should be redacted:
- Formatted:
url.path=a/[REDACTED].json - Structured:
format=json,token=[REDACTED]
Actual behavior
Formatted: "url.path":"a/[REDACTED].part2.part3.json"
Structured: "format":"json.secret","token":""
Regression?
No response
Known Workarounds
No response
Configuration
No response
Other information
No response
Activity
Fix route parameter redaction when the value contains the following l…
Fix route parameter redaction when the value contains the following l…