Click here to Skip to main content
Click here to Skip to main content

Preventive Method for URL Request Forgery- An Example with ASP.NET MVC

, 29 Nov 2010
Rate this:
Please Sign up or sign in to vote.
It is novel method to prevent the manipulation of parameter pass through the URL string

Introduction

No web applications are developed without concerning the security issues. But it is not sure whether the security measures are implemented correctly and the applications are well protected. We could see a lot of web applications query string parameters in plain string which are easy to manipulate and way to escalate restricted resources. Even the URL parameters are encrypted there is still a chance of a bruteforce attack by manipulating the encrypted hash and it yields when the database is large where the chance of matching a random hash is high. So it is clear the importance to protect the URL in the web application is necessary for all programming platforms. Here, this is discussed with reference to ASP.NET MVC and the utility class is presented.

Background

.NET Framework has a lot of improved methods for security and the MVC framework introduced new security methods like AntiForgeryToken. Let us have a brief look at the security methods in general practice.

  • Authorize attribute: This is useful to authorize a user or role to access any resource in the application after authenticated. This helps to have User and Role level protections.
  • Http Referrer Check: This is a general method not confined to .NET. This is to prevent an URL request which is not from the site, but from an external link or the link directly executed at the browser navigation bar.
  • Anti Forgery Token: This is a powerful option to prevent any hidden field manipulation while form posting and prevents Cross-Site Request Forgery
  • HTML Encoding: It is advised to encode all user inputs to prevent Cross Site Scripting attack/ XSS attack etc.,
  • Protection against SQL injections
  • Encryption of Query string parameters. This is good way to prevent manipulation of query string parameters. But this is not a complete protection, still vulnerable for a brute force attack.

Even though all the above methods are available, .NET framework doesn't provide an inbuilt functionality for protecting the URL parameters. Its URL authorization technique protects the whole URL, but not parts of the URL which are still vulnerable for manipulation. This piece of coding here helps to easily implement all these security methods with special importance to protecting the URL parameters. Creating a URL hash is not new but yet it is not a common practice in the industry though it is an important concern. So it would be important to share a piece of code for the easy implementation of this method in ASP.NET MVC.

Using the Code

The basic idea is simple, encrypting the URL parameters along with controller, action names to a hash and later it will be passed to server as a URL parameter. The hash will be verified at the server side for its validity by recomputing the hash and compared with the submitted hash. This hash is dynamic and specific to a session. So even though your URL cached in the man-in-the middle attack, it will not work in other sessions. Basically a utility class extends the HTML helper ActionLink class. So we can use Html.ActionLink method as such in the MVC implementation, in addition you only need to pass a boolean argument as a last argument to get the protected URL. It is pretty easy to use this coding. Examples are shown below.

(Source code attached: it is developed in Visual Studio Express 2010, SQL Express. This development platform is freely available.)

Try the demo project or include the ActionLinkExtensions.cs from the demo project to your project.

The figure shows the files having the core coding and those can be reused in your projects.

Add the following lines in your view below the page declarations:

Note
  • The boolean parameter passed in red color is the only additional parameter to get a protected URL, apart from the 10 overloaded parameters in the ASP MVC HTML. ActionLink method.
  • ExtendedHtmlActionLinkHelper - It is the namespace of this example project and it will change in your implementations.
  • RouteValues() and HtmlAttributes() are test classes that have test parameters and test attribute for hyperlink respectively. It changes in your implementations.

The Controller Code

Getting a protected URL in the view is easy as just passing a boolean parameter. Now let us see how to validate the URL when submitted to controller. It is even simpler. Just add this attribute to the controller method.

[IsValidURLRequest]

This attribute has taken care of the simple authorize check (user name, password verification), check for http referrer and protect the URL parameters.
However one more thing has to do with the routing for avoiding confusions with routes. Add the 'urltoken' as the first parameter in the route in global.asax and all the controller methods have this as the first argument. This can be an optional parameter. Route mapping example follows.

routes.MapRoute(
      "Default", // Route name
      "{controller}/{action}/{urltoken}/{id}", // URL with parameters
      new { controller = "Home", action = "Index", urltoken = UrlParameter.Optional, 
	id = UrlParameter.Optional } // Parameter defaults
);

The Model

This model code is completely reusable in your project. However, it depends on a controller named ErrorviewController and two action methods DisplayURLError and DisplayHttpReferrerError which need to be available in your project namespace.

All the model code for generating and validating the secure URL are in the file ActionLinkExtensions.cs, namespace SecuredUrl.Links in the demo project. This file can be included in any project and reused. Basically, it extends the HTML helper method 'ActionLink'. Nothing much to discuss about how to extend the HTML helper method as much information is available about this in CodeProject. Let us discuss how the URL hash is formed. Have a look at the following code:

//This is a static helper class which creates the URL Hash
public static class TokenUtility
{
       //This method creates the URL hash and attach it to the URL.
 public static string generateUrlToken(string controllerName, string actionName, 
 	RouteValueDictionary argumentParams, string password)
    {
        //The URL hash is dynamic by assign a dynamic key in each session. So
        //even though your URL is stolen, it will not work in other session
        if (HttpContext.Current.Session["url_dynamickey"] == null)
        {
            HttpContext.Current.Session["url_dynamickey"] = RandomString();
        }
        string token = "";
        //The salt include the dynamic session key and valid for an hour.
        string salt = HttpContext.Current.Session["url_dynamickey"].ToString() + 
        DateTime.Now.ToShortDateString() + " " + DateTime.Now.Hour; ;
        //generating the partial url
        string stringToToken = controllerName + "/" + actionName + "/";
        foreach (KeyValuePair<string, object> item in argumentParams)
        {
            if (item.Key != "controller" && 
            item.Key != "action" && item.Key != "urltoken")
            {
                stringToToken += "/" + item.Value;
            }
        }
        //Converting the salt in to a byte array
        byte[] saltValueBytes = System.Text.Encoding.ASCII.GetBytes(salt);
        //Encrypt the salt bytes with the password
        Rfc2898DeriveBytes key = new Rfc2898DeriveBytes(password, saltValueBytes);
        //get the key bytes from the above process
        byte[] secretKey = key.GetBytes(16);
        //generate the hash
        HMACSHA1 tokenHash = new HMACSHA1(secretKey);
        tokenHash.ComputeHash(System.Text.Encoding.ASCII.GetBytes(stringToToken));
        //convert the hash to a base64string
        token = Convert.ToBase64String(tokenHash.Hash).Replace("/","_");
        return token;
    }
    //This method validates the token
    public static bool validateToken(string token,string controllerName, 
    string actionName, RouteValueDictionary argumentParams, string password)
    {
        //compute the token for the current parameter
        string computedToken = generateUrlToken
        (controllerName, actionName, argumentParams, password);
        //compare with the submitted token
        if (computedToken != token)
        {
            computedToken = generateUrlToken
            ("", actionName, argumentParams, password);
        }
        else { return true; }
        
        if (computedToken != token)
        {
            return false;
        }
        else { return true; }
    }
    //It validates the token, where all the parameters passed as a RouteValueDictionary
    public static bool validateToken
	(RouteValueDictionary requestUrlParts, string password)
    {
        //get the parameters
        string controllerName;
        try
        {
            controllerName = Convert.ToString(requestUrlParts["controller"]);
        }
        catch (Exception e)
        {
            controllerName = "";
        }
        string actionName = Convert.ToString(requestUrlParts["action"]);
        string token = Convert.ToString(requestUrlParts["urltoken"]);
        //Compute a new hash
        string computedToken = generateUrlToken
        (controllerName, actionName, requestUrlParts, password);
        //compare with the submitted hash
        if (computedToken != token)
        {
            computedToken = generateUrlToken
            ("", actionName, requestUrlParts, password);
        }
        else { return true; }
        
        if (computedToken != token)
        {
            return false;
        }
        else { return true; }
    }
    //This method create the random dynamic key for the session
    private static string RandomString()
    {
        StringBuilder builder = new StringBuilder();
        Random random = new Random();
        char ch;
        for (int i = 0; i < 8; i++)
        {
            ch = Convert.ToChar(Convert.ToInt32
		(Math.Floor(26 * random.NextDouble() + 65)));
            builder.Append(ch);
        }
        return builder.ToString();
    }
}

//This is a attribute class which actually calls 
//the validation and to be used with the controller
public  class IsValidURLRequestAttribute : AuthorizeAttribute
{
    public override void OnAuthorization(AuthorizationContext filterContext)
    {
       // basic authorization check 
        base.OnAuthorization(filterContext);
        if (filterContext.HttpContext != null)
        {
            //Http referrer check and do the redirection if error occurs
            //It uses a controller named ErrorViewController and 
            //action named DisplayHttpReferrerError
            //These controller and action need to be present 
            //in your project in the project name space
            if (filterContext.HttpContext.Request.UrlReferrer == null)
            {
                filterContext.Result = new RedirectToRouteResult(
                     new System.Web.Routing.RouteValueDictionary
                    {
                            { "langCode", 
                            filterContext.RouteData.Values[ "langCode" ] },
                            { "controller", "ErrorView" },
                            { "action", "DisplayHttpReferrerError" },
                            { "ReturnUrl", filterContext.HttpContext.Request.RawUrl },
                    });
            }                    
        }
        
        /*Add code here to check the domain name the request come from*/
        
        // The call for validation of URL hash and do the redirection if error occurs
        //It uses a controller named ErrorViewController and action named DisplayURLError
        //These controller and action need to be present in your project 
        //in the project name space
        if (TokenUtility.validateToken
        (filterContext.RequestContext.RouteData.Values, 
		ActionLinkExtensions.tokenPassword) == false)
        {
            filterContext.Result = new RedirectToRouteResult(
                 new System.Web.Routing.RouteValueDictionary
                    {
                            { "langCode", filterContext.RouteData.Values[ "langCode" ] },
                            { "controller", "ErrorView" },
                            { "action", "DisplayURLError" },
                            { "ReturnUrl", filterContext.HttpContext.Request.RawUrl }
                    });
        }
    }
}

The above code is self explanatory. It creates a hash with a session depend key and attaches it to the URL. There is a validation attribute which validates for basic authorization, HTTPReferrer and the URL hash.

Proof Of Concept

Run the demo application. Click any links in the page. If not logged in, then it will redirect to login page. For the next test, Log on (register yourself or use this login user: albin; pass: test123) and then copy the link into the navigation bar in a new tab in the browser, which is one of the ways to manipulate the parameter (forged) and execute the link. It will not pass for httpreferrer check. As I notified in the code to check external links, add the code to check the domain from http referrer. Now inject this JavaScript.

(Once logged in, paste the following JavaScript in the navigation bar and hit enter key. It will create a "Test Me" link.).

javascript:alert(document.getElementById("testplace").innerHTML="<a href=
	'<right click the link in the demo page-> copy link location than paste here. 
	Manipulate the parameter '>Test Me</a>");

Replace URL in the JavaScript with the one copied from the demo page and manipulate the parameter and then execute as said above.

For example, click the link "Link Test 2". It opens the next page and shows the result:

Right click the link and copy the link location:

Change the parameter.

javascript:alert(document.getElementById("testplace").innerHTML=
"<a href='http://localhost:59248/Home/handleLink/fjr2orS7nxTDjyetxJRb%2brJOd_k%3d/200>
Test Me</a>");

Now paste it in the nav bar and hit enter. Click ok on the alert message. You will get a "Test me" link as shown below:

Now click the "Test Me" link. The result will be

This passes the httpreferrer check, but fails at URL hash check as the URL parameter is manipulated. If this checking is not done, the hacker might get access to other users data. This URL hash is session specific. Close your browser and re-run the demo. You can find out the hashes are changed.

Conclusively, this URL hash method provides a good security against URL parameter forgery. If you really want to make the hacker's life hell, use a combination of encrypted parameters and this URL hashing method.

Be secure!!

Points of Interest

I have written this small article to make use of this method in the common web applications, because I came across many sites which pass parameters in the URL unsecured. So I thought of making this method more common and I chose popular CodeProject to publish it. As a final word, if there are errors, please forgive me. In my next article, I will write about how this can be implemented in Ajax requests.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

Albin Abel
Software Developer
India India
I am developer in .Net and GIS. albin_gis@yahoo.com

Comments and Discussions

 
Generalg 1 PinmemberPranay Rana14-Dec-10 20:37 
GeneralMy vote of 5 PinmemberShahriar Iqbal Chowdhury30-Nov-10 10:39 
GeneralMy vote of 4 PingroupSebastien Termote29-Nov-10 23:36 
QuestionMy vote of 5 Pinmembersjelen29-Nov-10 7:09 
AnswerRe: My vote of 5 PinmemberAlbinAbel29-Nov-10 17:49 
GeneralMy vote of 5 PinmemberCesar to Dnn29-Nov-10 5:17 
GeneralMy vote of 5 PinmemberEric Xue (brokensnow)26-Nov-10 13:10 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web04 | 2.8.140721.1 | Last Updated 30 Nov 2010
Article Copyright 2010 by Albin Abel
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid