aspnetcore: Inconsistent URL decoding of HttpRequest.Path with percent-encoded characters

Describe the bug

HttpContext.Request.Path seems to decode percent-encoded URLs sometimes, but not all the time. The documentation says nothing about encoding. I expect it to never decode the path, but testing on ASP.NET Core 3.1 I get results like this:

Request URL context.Request.Path Percent decoded?
http://localhost:5000/test%24 /test$ Yes
http://localhost:5000/test%24aa /test$aa Yes
http://localhost:5000/test%25a /test%25a No
http://localhost:5000/test%25aa /test%aa Yes
http://localhost:5000/test%20aa /test%20aa No

To Reproduce

Create an empty ASP.NET Core project and change the Startup class to

    public class Startup
    {
        public void ConfigureServices(IServiceCollection services)
        {
        }

        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            app.Use(async (context, next) =>
            {
                Console.WriteLine("Request path = " + context.Request.Path);
                await context.Response.WriteAsync("Request path = " + context.Request.Path);
            });
        }
    }

Program class unchanged from the template:

    public class Program
    {
        public static void Main(string[] args)
        {
            CreateHostBuilder(args).Build().Run();
        }

        public static IHostBuilder CreateHostBuilder(string[] args) => Host.CreateDefaultBuilder(args).ConfigureWebHostDefaults(webBuilder => { webBuilder.UseStartup<Startup>(); });
    }

Then send the above GET requests to it from a web browser or curl.

Further technical details

  • ASP.NET Core version: 3.1
  • dotnet --info:
dotnet --info
.NET SDK (reflecting any global.json):
 Version:   5.0.200-preview.21079.7
 Commit:    580b1c3770

Runtime Environment:
 OS Name:     Windows
 OS Version:  6.1.7601
 OS Platform: Windows
 RID:         win7-x64
 Base Path:   C:\Program Files\dotnet\sdk\5.0.200-preview.21079.7\

Host (useful for support):
  Version: 5.0.3
  Commit:  c636bbdc8a

.NET SDKs installed:
  3.0.100 [C:\Program Files\dotnet\sdk]
  5.0.102 [C:\Program Files\dotnet\sdk]
  5.0.103 [C:\Program Files\dotnet\sdk]
  5.0.200-preview.21079.7 [C:\Program Files\dotnet\sdk]

.NET runtimes installed:
  Microsoft.AspNetCore.All 2.1.25 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.All]
  Microsoft.AspNetCore.App 2.1.25 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
  Microsoft.AspNetCore.App 3.1.12 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
  Microsoft.AspNetCore.App 5.0.3 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
  Microsoft.NETCore.App 2.0.9 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.NETCore.App 2.1.25 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.NETCore.App 3.1.12 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.NETCore.App 5.0.3 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.WindowsDesktop.App 3.1.12 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]
  Microsoft.WindowsDesktop.App 5.0.3 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]
  • The IDE (VS / VS Code/ VS4Mac) you’re running on, and its version: VS 16.8.6
  • OS: Windows 7 x64

About this issue

  • Original URL
  • State: open
  • Created 3 years ago
  • Comments: 19 (10 by maintainers)

Most upvoted comments

@mbrenn this issue is specific to the path and %2F. Please open a new issue with the details for your query issue.

It is actually possible to get the raw original path with httpContext.Features.Get<IHttpRequestFeature>()?.RawTarget (RawTarget). However this does also include the full query string, and as the docs say, the target may not always be a path (e.g. for server-wide OPTIONS requests).

Examples from a Kestrel app:

Request URL Request.Path.Value Request.Path.ToString() IHttpRequestFeature.RawTarget
http://localhost:5000/a/b%20c /a/b c /a/b%20c /a/b%20c
http://localhost:5000/a%2fb%20c /a%2fb c /a%2fb%20c /a%2fb%20c
http://localhost:5000/a%252fb%20c /a%2fb c /a%2fb%20c /a%252fb%20c

Edit: you’d also need to be careful around dots in the path, as this can lead to path traversal attacks:

Request URL Request.Path.Value Request.Path.ToString() IHttpRequestFeature.RawTarget
http://localhost:5000/a/./../... /... /... /a/./../...
http://localhost:5000/a/%2e/.%2e/..%2e /... /... /a/%2e/.%2e/..%2e