【问题标题】:IIS treats double-encoded forward slashes in URLs differently on the first request than it does on subsequent requestsIIS 在第一个请求中处理 URL 中的双编码正斜杠的方式与对后续请求的处理方式不同
【发布时间】:2011-08-31 00:22:49
【问题描述】:

最近,我的团队被要求为 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

第二个请求看起来像:

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 首次启动时能够看到它

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

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(因为如果我们解析 Raw Url 而不是使用 .NET 提供的“安全”URL,我们将不得不担心很多其他问题),我们还有什么其他方法可以处理这个问题?

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

【问题讨论】:

    标签: c# .net asp.net-mvc iis httpmodule


    【解决方案1】:

    Request.Url 已经可以解码 - 我不相信你在做什么。

    查看内部详细信息: Querystring with url-encoded ampersand prematurely decoded within Request.Url

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

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

    【讨论】:

    • 好的,检查头 CACHE_URL 怎么样?如果它被解码,那么当你得到它时它的 IIS 或浏览器行为。查看stackoverflow.com/questions/757806/… 了解更多信息
    • 如果它没有被解码并且 HTTP_URL 是,那么它的 IIS 正在做编码。如果两者都未编码,那么可能都是 asp.net。
    【解决方案2】:

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

    我通过 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;
        }
    }
    

    【讨论】:

    • 这不是说如果路径以 / 开头,就把它砍掉吗?这里没有解码。
    • @RandomEngy 这也是我问题的症结所在:为什么第一个请求与第二个请求不同。我将查看其他答案和其他 cmets 并尝试那里列出的内容;但这是困扰我的部分,行为从第一个请求变为第二个请求。它似乎不应该,除非 IIS 第一次处理它,并为每个后续请求将其推送到 .NET(因为也许 .NET 仍在为第一个请求旋转?)
    • ASP.NET 总是处理所有事情,即使是第一个请求。我的猜测是某些初始化代码直接或间接导致了这种行为。
    • 不是,它每次都会执行,但是您正在查看整个类的一个属性,以及大量可能每次都无法正常工作的东西。反思 this.PrivateAbsolutePath,你就会明白我的意思。
    猜你喜欢
    • 1970-01-01
    • 2018-05-07
    • 1970-01-01
    • 2015-10-03
    • 2017-10-16
    • 1970-01-01
    • 1970-01-01
    • 2021-04-25
    • 2015-12-20
    相关资源
    最近更新 更多