Недавно мою команду попросили реализовать HttpModule для приложения ASP.NET MVC, которое обрабатывает URL с двойным кодированием в IIS 7 и .NET 3.5. Вот суть проблемы:
Иногда мы получаем URL-адреса с двойными косыми чертами в виде двойного кодирования, которые выглядят так:
http://www.example.com/%252fbar%5cbaz/foo
Существуют и другие форматы, которые мы должны обрабатывать, но у них всех есть что-то общее, у них есть двойная косая черта.
Чтобы исправить это, мы написали HttpModule, который действует только тогда, когда URL имеет двойную косую черту, и мы перенаправляем его на нормальный URL. Детали не важны, но есть два бита:
- Мы не можем контролировать тот факт, что эти URL-адреса имеют двойную косую черту
- И мы еще не обновились до .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-адресу, и вероятность того, что это произойдет относительно низкие.