Normally, when a site requires that you are logged in before you can access a certain page, you are taken to the login screen and after successfully authenticating yourself, you are redirected back to the originally requested page. This is great for usability - but without careful scrutiny, this feature can easily become an open redirect vulnerability.
Sadly, for an example of this vulnerability, look no further than the default LogOn action provided by 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);
}
If a user is successfully authenticated, they are redirected to "returnUrl" (if it was provided via the login form submission).
Here is a simple example attack (one of many, actually) that exploits this vulnerability:
http://www.mybank.com/logon?returnUrl=http://www.badsite.com
http://www.badsite.com
which is made to look exactly like victim's bank's website, so victim doesn't know he is now on a different site.http://www.badsite.com
says something like "We need to update our records - please type in some extremely personal information below: [ssn], [address], [phone number], etc."Any ideas on how to maintain this redirect-on-successful-login functionality yet avoid the open-redirect vulnerability?
I'm leaning toward the option of splitting the "returnUrl" parameter into controller/action parts and use "RedirectToRouteResult" instead of simply "Redirect". Does this approach open any new vulnerabilities?
Update
By limiting myself to controller/action routes, I can't redirect to custom routes (e.g. /backend/calendar/2010/05/21
). I know that by passing more parameters to the LogOn action, I could get it to work, but I feel like I'll always be revisiting this method -- keeping it up to date with our routing scheme. So, instead of splitting the returnUrl into its controller/action parts, I am keeping returnUrl as-is and parsing it to make sure it contains only a relative path (e.g., /users/1
) and not an absolute path (e.g., http://www.badsite.com/users/1
). Here is the code I'm using:
private static bool CheckRedirect(string url) {
try {
new Uri(url, UriKind.Relative);
}
catch (UriFormatException e) {
return false;
}
return true;
}
Side note: I know this open-redirect may not seem to be a big deal compared to the likes of XSS and CSRF, but us developers are the only thing protecting our customers from the bad guys - anything we can do to make the bad guys' job harder is a win in my book.
Thanks, Brad
Preventing Unvalidated Redirects and Forwards Where possible, have the user provide short name, ID or token which is mapped server-side to a full target URL. This provides the highest degree of protection against the attack tampering with the URL.
Open redirect vulnerability in the software allows remote attackers to redirect users to arbitrary web sites and conduct phishing attacks via a URL in the proper parameter. Assume all input is malicious.
Summary. Open redirects are one of the OWASP 2010 Top Ten vulnerabilities. This check looks at user-supplied input in query string parameters and POST data to identify where open redirects might be possible.
Jon Galloway wrote up an article with a solution for MVC 2 (and 1).
Here's the snippet that should help with your issue:
SECURED (original article updated 2014)
private bool IsLocalUrl(string url)
{
return System.Web.WebPages.RequestExtensions.IsUrlLocalToHost(
RequestContext.HttpContext.Request, url);
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With