IIS обрабатывает двойные кодированные прямые косые черты в URL по-разному при первом запросе, чем при последующих запросах - PullRequest
8 голосов
/ 31 августа 2011

Недавно мою команду попросили реализовать HttpModule для приложения ASP.NET MVC, которое обрабатывает URL с двойным кодированием в IIS 7 и .NET 3.5. Вот суть проблемы:

Иногда мы получаем 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.
    }
}

Затем вызовите тот же URL на localhost:

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

NB. Обязательно вставьте Debugger.Launch() вызов перед линией в context_BeginRequest, чтобы вы могли видеть его при первом запуске 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-адрес вместо использования «безопасного» URL-адреса, предоставленного .NET), какие есть другие методы для этого

Имейте в виду, физическое влияние этой проблемы невелико: чтобы это было реальной проблемой, первый запрос клиента к веб-серверу должен был быть выполнен по указанному выше определенному URL-адресу, и вероятность того, что это произойдет относительно низкие.

Ответы [ 2 ]

2 голосов
/ 31 августа 2011

Request.Url уже может быть декодирован - я бы не стал доверять тому, что вы делаете.

См. Внутренние детали по адресу: Строка запроса с амперсандом в кодировке URL, преждевременно декодированная в Request.Url

Решение состоит в том, чтобы получить доступ к значениям напрямую через Request.RawUrl.

Я понимаю, что ваша проблема связана с путем, но, похоже, происходит то же самое. Попробуйте RawUrl - посмотрите, работает ли он вместо вас.

0 голосов
/ 31 августа 2011

Это действительно не ответ, а, возможно, шаг в правильном направлении.У меня не было времени, чтобы создать тестовый жгут, чтобы что-то доказать.

Я последовал за 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;
    }
}
...