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

本文关键字:请求 处理 方式 方式不 URL 第一个 IIS 编码 | 更新日期: 2023-09-27 18:07:00

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

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

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

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

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

  1. 我们无法控制这些网址具有双编码正斜杠的事实
  2. 我们还没有转向 .NET 4.0,它也没有出现在近期的地平线上。

问题是这样的:

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

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

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

第二个请求如下所示:

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.
    }
}

然后,在本地主机上调用相同的 URL:

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

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

执行第一个请求时,应看到:

http://example.com/bar/foo

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

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

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

另外:第一个请求是否针对双编码 URL 并不重要,第二个请求将始终由 IIS 适当处理(或者至少与处理双编码正斜杠一样合适(。问题在于第一个请求。

更新

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

第一个请求
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在第一个请求上仍部分编码。

第二个请求
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(因为如果我们解析原始 URL 而不是使用 .NET 提供的"安全"URL,我们将不得不担心许多其他问题(,我们还有什么其他方法可以处理这个问题?

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

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

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

有关内部详细信息,请访问:具有 url 编码的 & 符号的查询字符串,并在 Request.URL 中过早解码

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

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

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

我跟着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;
    }
}