Обычно, когда сайт требует, чтобы вы вошли в систему, прежде чем вы сможете получить доступ к определенной странице, вы попадете на экран входа в систему и после успешной аутентификации вы будете перенаправлены обратно на первоначально запрошенную страницу. Это очень удобно для удобства использования, но без тщательного изучения эта функция может легко стать уязвимостью open redirect .
К сожалению, в качестве примера этой уязвимости смотрите не дальше, чем действие LogOn по умолчанию, предоставляемое ASP.NET MVC 2:
[HttpPost]
public ActionResult LogOn(LogOnModel model, string returnUrl)
{
if (ModelState.IsValid) {
if (MembershipService.ValidateUser(model.UserName, model.Password)) {
FormsService.SignIn(model.UserName, model.RememberMe);
if (!String.IsNullOrEmpty(returnUrl)) {
return Redirect(returnUrl); // open redirect vulnerability HERE
} else {
return RedirectToAction("Index", "Home");
}
} else {
ModelState.AddModelError("", "User name or password incorrect...");
}
}
return View(model);
}
Если пользователь успешно прошел аутентификацию, он перенаправляется на «returnUrl» (если это было предоставлено посредством отправки формы входа в систему).
Вот простой пример атаки (одной из многих, на самом деле), которая использует эту уязвимость:
- Атакующий, притворяющийся банком жертвы, отправляет жертве электронное письмо со ссылкой, например:
http://www.mybank.com/logon?returnUrl=http://www.badsite.com
- После того, как учили проверять ВСЕ доменное имя (например, google.com = GOOD, google.com.as31x.example.com = BAD), жертва знает, что ссылка в порядке - никаких хитростей Фишинг домена продолжается.
- Жертва нажимает на ссылку, видит свой фактический знакомый банковский сайт и получает запрос на вход
- Жертва входит в систему и впоследствии перенаправляется на
http://www.badsite.com
, который выглядит как веб-сайт банка жертвы, поэтому жертва не знает, что он сейчас на другом сайте.
http://www.badsite.com
говорит что-то вроде: «Нам нужно обновить наши записи. Пожалуйста, введите ниже крайне личную информацию: [ssn], [адрес], [номер телефона] и т. Д.»
- Жертва, все еще думая, что он находится на своем банковском веб-сайте, падает на уловку и предоставляет злоумышленнику информацию
Есть какие-нибудь идеи о том, как сохранить эту функцию перенаправления при успешном входе в систему, но избежать уязвимости открытого перенаправления?
Я склоняюсь к возможности разделения параметра «returnUrl» на части контроллера / действия и использую «RedirectToRouteResult» вместо простого «Redirect». Раскрывает ли этот подход какие-либо новые уязвимости?
Обновление
Ограничив себя маршрутами контроллера / действия, я не могу перенаправить на пользовательские маршруты (например, /backend/calendar/2010/05/21
). Я знаю, что, передав больше параметров в действие LogOn, я мог бы заставить его работать, но я чувствую, что всегда буду пересматривать этот метод - обновлять его в соответствии с нашей схемой маршрутизации. Таким образом, вместо разделения returnUrl на части контроллера / действия я сохраняю returnUrl как есть и анализирую его, чтобы убедиться, что он содержит только относительный путь (например, /users/1
), а не абсолютный путь (например, * 1045). *). Вот код, который я использую:
private static bool CheckRedirect(string url) {
try {
new Uri(url, UriKind.Relative);
}
catch (UriFormatException e) {
return false;
}
return true;
}
Примечание: я знаю, что это открытое перенаправление может показаться не слишком важным делом по сравнению с XSS и CSRF, но мы, разработчики, единственное, что защищает наших клиентов от плохих парней - все, что мы можем сделать работу плохих парней труднее - победа в моей книге.
Спасибо, Брэд