Обычно, когда сайт требует, чтобы вы вошли в систему, прежде чем вы сможете получить доступ к определенной странице, вы попадаете на экран входа в систему и после успешной аутентификации вы перенаправляетесь обратно на первоначально запрошенную страницу. Это отлично подходит для удобства использования - но без тщательного изучения эта функция может легко стать уязвимостью 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
- Будучи обученным проверять имя домена ENTIRE (например, google.com = GOOD, google.com.as31x.example.com = BAD), жертва знает, что ссылка в порядке - нет никаких сложных подкатегорий, фишинг домена продолжается.
- Потребитель щелкает ссылку, видит свой фактический знакомый банковский сайт и просят войти в систему
- Жертва регистрируется и впоследствии перенаправляется на
http://www.badsite.com
, которая сделана так, чтобы выглядеть как сайт банка жертвы, поэтому жертва не знает, что он сейчас находится на другом сайте. -
http://www.badsite.com
говорит что-то вроде "Нам нужно обновить наши записи", пожалуйста, введите некоторые личную информацию ниже: [ssn], [адрес], [номер телефона] и т.д. " - Жертва, все еще думающая, что находится на своем банковском веб-сайте, попадает на уловку и предоставляет злоумышленнику информацию.
Любые идеи о том, как поддерживать эту функцию переадресации при успешном подключении, все же избегают уязвимости с открытым перенаправлением?
Я склоняюсь к варианту разделения параметра returnUrl на элементы управления/действия и используйте "RedirectToRouteResult" вместо простого "Перенаправления". Открывает ли этот подход какие-либо новые уязвимости?
Обновление
Ограничиваясь маршрутами контроллера/действий, я не могу перенаправить на пользовательские маршруты (например, /backend/calendar/2010/05/21
). Я знаю, что, передавая больше параметров в действие LogOn, я мог бы заставить его работать, но я чувствую, что всегда буду пересматривать этот метод - постоянно обновляя нашу схему маршрутизации. Таким образом, вместо того, чтобы разбивать returnUrl на его элементы управления/действия, я сохраняю returnUrl as-is и анализирую его, чтобы убедиться, что он содержит только относительный путь (например, /users/1
), а не абсолютный путь (например, http://www.badsite.com/users/1
). Вот код, который я использую:
private static bool CheckRedirect(string url) {
try {
new Uri(url, UriKind.Relative);
}
catch (UriFormatException e) {
return false;
}
return true;
}
Боковое примечание: я знаю, что этот open-redirect может показаться не таким большим, как XSS и CSRF, но мы разработчики - единственное, что защищает наших клиентов от плохих парней - все, что мы можем сделать, чтобы сделать плохая работа парней - победа в моей книге.
Спасибо, Брэд