Doing my usual random code browsing yesterday I stumbled on a method that peaked my curiosity. The intent of the method is to only allow redirect to relative paths. If the redirect is not a relative path, it is rejected. Let’s look at the implementation.

public static void SafeRedirect(HttpResponse response, string rawRelativeUrl)

{

    Uri tmpUrl;

 

    // we use the default action of redirecting to

    // the root if the url is not defined

    if (response == null)

    {

        throw new ArgumentNullException("response");

    }

 

    if (rawRelativeUrl == null)

    {

        throw new ArgumentNullException("rawRelativeUrl");

    }

 

    if (rawRelativeUrl.Trim().Length == 0)

    {

        throw new ArithmeticException("rawRelativeUrl must not be empty");

    }

 

    // this method will parse the string into a url so we can make

    // sure it is a relative.  This protects against open redirects

    if (Uri.TryCreate(rawRelativeUrl, UriKind.Relative, out tmpUrl))

        response.Redirect(rawRelativeUrl);

}

Looking at the code, we can see that each parameter is validated and it makes use of the System.Uri class and only redirect if the rawRelativeUrl is relative. The issue with this code is that it doesn’t handle scheme relative paths. Scheme relative paths are basically a relative url that doesn’t include the scheme section. For example //www.microsoft.com is a scheme relative url that will redirect to {base scheme}//www.microsoft.com.

 

This means that if the application is hosted under http://www.mysite.com and the rawRelativeUrl is //www.microsoft.com, it will allow the redirect and the browser will be directed to http://www.microsoft.com. This is actually what SafeRedirect is trying to protect against.

 

To remediate the issue, the code needs to handle the scheme relative url case by rejecting rawRelativeUrl if it begins with the double forward slash ‘//’.

public static void SafeRedirect(HttpResponse response, string rawRelativeUrl)

{

    Uri tmpUrl;

 

    // we use the default action of redirecting to the root if the url is not defined

    if (response == null)

    {

        throw new ArgumentNullException("response");

    }

 

    if (rawRelativeUrl == null)

    {

        throw new ArgumentNullException("rawRelativeUrl");

    }

 

    if (rawRelativeUrl.Trim().Length == 0)

    {

        throw new ArithmeticException("rawRelativeUrl must not be empty");

    }

 

 

    // Reject scheme relative url

    if (rawRelativeUrl.StartsWith("//"))

        throw new ArgumentException("scheme relative url is not allow", "rawRelativeUrl");

 

    // this method will parse the string into a url so we can make

    // sure it is a relative.  This protects against open redirects

    if (Uri.TryCreate(rawRelativeUrl, UriKind.Relative, out tmpUrl))

        response.Redirect(rawRelativeUrl);

}