IIS 对第一个请求和后续请求中 URL 中双编码正斜杠的处理方式不同

发布于 2024-12-02 05:27:30 字数 3744 浏览 2 评论 0原文

最近,我的团队被要求为 ASP.NET MVC 应用程序实现一个 HttpModule,该应用程序处理 IIS 7 和 .NET 3.5 上的双编码 URL。问题的关键在于:

我们有时会得到具有双编码正斜杠的 URL,如下所示:

http://www.example.com/%252fbar%5cbaz/foo

我们还必须处理其他格式,但它们都有一些共同点,它们都有一个双编码正斜杠削减。

为了解决这个问题,我们编写了一个 HttpModule,它仅在 URL 具有双编码正斜杠时才起作用,并且我们将其重定向到正常的 URL。细节并不重要,但有两点是:

  1. 我们无法控制这些 URL 具有双重编码的正斜杠这一事实
  2. 我们还没有升级到 .NET 4.0,也不会立即升级。

问题是:

IIS 启动后的第一个请求显示的 URL 与第二个请求不同。

如果我们使用上面示例中的 URL,则对 IIS 的第一个请求将如下所示:

http://www.example.com/bar/baz/foo

,第二个请求将如下所示:

< code>http://www.example.com/%252fbar%5cbaz/foo

这是通过在调试时检查 Application.Request.Url.AbsolutePath 属性来完成的。

下面是应该重现该问题的最小代码示例(创建一个新的 MVC 应用程序,并注册以下 HttpModule):

public class ForwardSlashHttpModule : IHttpModule
{
    internal IHttpApplication Application { get; set; }
    
    public void Dispose()
    {
        Application = null;
    }

    public void Init(HttpApplication context)
    {
        Initialize(new HttpApplicationAdapter(context));
    }
    
    internal void Initialize(IHttpApplication context)
    {
        Application = context;
        context.BeginRequest += context_BeginRequest;
    }
    
    internal void context_BeginRequest(object sender, EventArgs e)
    {
        var url = Application.Request.Url.AbsolutePath; //<-- Problem point
        //Do stuff with Url here.
    }
}

然后,在 localhost 上调用相同的 URL:

http://www.example.com/%252fbar%5c/foo

注意:请确保在 context_BeginRequest 行之前插入 Debugger.Launch() 调用,以便您能够在 IIS 首次启动时看到它

注意:请确保在 context_BeginRequest 行之前插入 Debugger.Launch() 调用,以便在 IIS 第一次启动时执行时 第一个请求,您应该看到:

http://example.com/bar/foo

在后续请求中,您应该看到:

http://example.com//bar/foo< /代码>。

我的问题是:这是 IIS 中的错误吗?为什么第一次调用 Application.Request.Url.AbsolutePath 时会提供不同的 URL,但后续请求不会提供不同的 URL?

另外:无论第一个请求是否是双重编码的 URL,第二个请求始终都会由 IIS 进行适当的处​​理(或者至少按照处理双重编码的正斜杠的方式进行处理)。问题就出在第一个请求上。

更新

我尝试了几个不同的属性来查看第一个请求是否有不同的值:

First Request
string u = Application.Request.Url.AbsoluteUri;
"http://example.com/foo/baz/bar/"
string x = Application.Request.Url.OriginalString;
"http://example.com:80/foo/baz/bar"
string y = Application.Request.RawUrl;
"/%2ffo/baz/bar"
bool z = Application.Request.Url.IsWellFormedOriginalString();
true

唯一有趣的是 Application.Request.RawUrl 发出单编码的正斜杠(% 2f),并将编码的反斜杠(%5c)转换为正斜杠(尽管其他所有东西也这样做)。

RawUrl 仍然在第一个请求时进行部分编码。

Second Request
string u = Application.Request.Url.AbsoluteUri;
"http://example.com//foo/baz/bar"
string x = Application.Request.Url.OriginalString;
"http://example.com:80/%2ffoo/baz/bar"
string y = Application.Request.RawUrl;
"/%2ffoo/baz/bar"
bool z = Application.Request.Url.IsWellFormedOriginalString();
false

第二个请求中的有趣点:

  • IsWellFormedOriginalString()false。在第一个请求中,它是true
  • RawUrl 是相同的(可能有帮助)。
  • AbsoluteUri 是不同的。在第二个请求中,它有两个正斜杠。

更新

Application.Request.ServerVariables["URL"] = /quotes/gc/v12/CMX
Application.Request.ServerVariables["CACHE_URL"] = http://example.com:80/%2ffoo/baz/bar

未解决的问题

  • 这似乎是 IIS 或 .NET 中的错误。是吗?
  • 这只对应用程序在 iisreset 之后发出的第一个请求很重要。
  • 除了使用 RawUrl 之外(因为如果我们解析Raw Url 而不是使用 .NET 提供的“安全”URL),我们还有哪些其他方法可以处理这个问题?

请记住,此问题的物理影响很低:要使其成为实际问题,客户端向 Web 服务器发出的第一个请求必须针对上述特定 URL,并且发生这种情况的可能性相对较低。

Recently my team was asked to implement an HttpModule for an ASP.NET MVC application that handled double-encoded URLs on IIS 7 and .NET 3.5. Here's the crux of the problem:

We sometimes get URLs that have double-encoded forward slashes that look like so:

http://www.example.com/%252fbar%5cbaz/foo

There are other formats that we have to handle as well, but they all have something in common, they have a double-encoded forward slash.

To fix this, we wrote an HttpModule that only acts when a URL has a double encoded forward slash, and we redirect it to a sane URL. The details aren't important, but there are two bits that are:

  1. We can't control the fact that these URLs have double-encoded forward slashes
  2. And we have not ugpraded to .NET 4.0 yet, nor is it on the immediate horizon.

Here's the problem:

The first request after IIS starts up shows a different URL than the second request does.

If we used the URL from the above example, the first request to IIS would look like:

http://www.example.com/bar/baz/foo

and the second request would look like:

http://www.example.com/%252fbar%5cbaz/foo

This was done by inspecting the Application.Request.Url.AbsolutePath property while debugging.

Here's the smallest code example that should reproduce the problem (create a new MVC application, and register the following HttpModule):

public class ForwardSlashHttpModule : IHttpModule
{
    internal IHttpApplication Application { get; set; }
    
    public void Dispose()
    {
        Application = null;
    }

    public void Init(HttpApplication context)
    {
        Initialize(new HttpApplicationAdapter(context));
    }
    
    internal void Initialize(IHttpApplication context)
    {
        Application = context;
        context.BeginRequest += context_BeginRequest;
    }
    
    internal void context_BeginRequest(object sender, EventArgs e)
    {
        var url = Application.Request.Url.AbsolutePath; //<-- Problem point
        //Do stuff with Url here.
    }
}

Then, call the same URL on localhost:

http://www.example.com/%252fbar%5c/foo

NB: Make sure to insert a Debugger.Launch() call before the line in context_BeginRequest so that you'll be able to see it the first time IIS launches

When you execute the first request, you should see:

http://example.com/bar/foo

on subsequent requests, you should see:

http://example.com//bar/foo.

My question is: Is this a bug in IIS? Why does it provide different URLs when calling Application.Request.Url.AbsolutePath the first time, but not for any subsequent request?

Also: It doesn't matter whether the first request is for a double encoded URL or not, the second request will always be handled appropriately by IIS (or at least, as appropriate as handling double-encoded forward slashes can be). It's that very first request that is the problem.

Update

I tried a few different properties to see if one had different values on the first request:

First Request

string u = Application.Request.Url.AbsoluteUri;
"http://example.com/foo/baz/bar/"
string x = Application.Request.Url.OriginalString;
"http://example.com:80/foo/baz/bar"
string y = Application.Request.RawUrl;
"/%2ffo/baz/bar"
bool z = Application.Request.Url.IsWellFormedOriginalString();
true

The only interesting thing is that the Application.Request.RawUrl emits a single-encoded Forward slash (%2f), and translates the encoded backslash (%5c) to a forwardslash (although everything else does that as well).

The RawUrl is still partially encoded on the first request.

Second Request

string u = Application.Request.Url.AbsoluteUri;
"http://example.com//foo/baz/bar"
string x = Application.Request.Url.OriginalString;
"http://example.com:80/%2ffoo/baz/bar"
string y = Application.Request.RawUrl;
"/%2ffoo/baz/bar"
bool z = Application.Request.Url.IsWellFormedOriginalString();
false

Interesting points from the second request:

  • IsWellFormedOriginalString() is false. On the first request it was true.
  • The RawUrl is the same (potentially helpful).
  • The AbsoluteUri is different. On the second request, it has two forward slashes.

Update

Application.Request.ServerVariables["URL"] = /quotes/gc/v12/CMX
Application.Request.ServerVariables["CACHE_URL"] = http://example.com:80/%2ffoo/baz/bar

Open Questions

  • This seems like a bug in either IIS or .NET. Is it?
  • This only matters for the very first request made by an application after an iisreset
  • Besides using RawUrl (as we'd have to worry about a lot of other problems if we parsed the Raw Url instead of using the 'safe' URL provided by .NET), what other methods are there for us to handle this?

Keep in mind, the physical impact of this problem is low: For it to be an actual problem, the first request to the web server from a client would have to be for the above specific URL, and the chances of that happening are relatively low.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

甚是思念 2024-12-09 05:27:30

Request.Url 已经可以解码 - 我不相信它会做你正在做的事情。

内部详细信息请参见:
带有 url 编码和符号的查询字符串在 Request.Url 中过早解码

解决方案是直接通过Request.RawUrl 访问值。

我意识到你的问题与路径有关,但似乎发生了同样的事情。尝试 RawUrl - 看看它是否适合您。

Request.Url can be decoded already - I wouldn't trust it for what you are doing.

See the internal details at:
Querystring with url-encoded ampersand prematurely decoded within Request.Url

The solution is to access the values directly via Request.RawUrl.

I realize your prob is with the path, but it seems the same thing is going on. Try the RawUrl - see if it works for you instead.

绝對不後悔。 2024-12-09 05:27:30

这确实不是一个答案,但可能是朝着正确方向迈出的一步。我没有时间创建测试工具来证明任何事情。

我通过 Reflector 跟踪了 this.PrivateAbsolutePath 并且它一直持续下去。访问时有很多字符串操作。

public string AbsolutePath
{
    get
    {
        if (this.IsNotAbsoluteUri)
        {
            throw new InvalidOperationException(SR.GetString("net_uri_NotAbsolute"));
        }
        string privateAbsolutePath = this.PrivateAbsolutePath; //HERE
        if (this.IsDosPath && (privateAbsolutePath[0] == '/'))
        {
            privateAbsolutePath = privateAbsolutePath.Substring(1); 
        }
        return privateAbsolutePath;
    }
}

This really isn't an answer, but possibly a step in the right direction. I haven't had time to create a test harness to prove anything.

I followed this.PrivateAbsolutePath through Reflector and it goes on and on. There is a lot of string manipulation when it's accessed.

public string AbsolutePath
{
    get
    {
        if (this.IsNotAbsoluteUri)
        {
            throw new InvalidOperationException(SR.GetString("net_uri_NotAbsolute"));
        }
        string privateAbsolutePath = this.PrivateAbsolutePath; //HERE
        if (this.IsDosPath && (privateAbsolutePath[0] == '/'))
        {
            privateAbsolutePath = privateAbsolutePath.Substring(1); 
        }
        return privateAbsolutePath;
    }
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文