@Blog.Author(Nandip Makwana) .LearningExperience(ASP.NET, ASP.NET MVC, IIS, jQuery & Technology Surrounding it...)

January 1, 2013 comment ,

ASP.NET MVC Value Provider for encrypted query string

By looking at the post title, it is quite clear that we are about to discuss, query string encryption in ASP.NET MVC application. But I am more interested in demonstrating extensibility of ASP.NET MVC framework and how we can leverage it by plugging our custom logic into ASP.NET MVC life cycle.

As always let me start with scratch, recently I came across a ASP.NET MVC application, where several action method were accepting query string parameter and later we identified that few of them must accept encrypted query string parameter. Along with this I also wanted to make sure that...

  • We don’t want to decrypt query string manually in each action method as there are numerous number of action method were already implanted and it could be increase in further development.
  • Model binding must not break due to this encryption because application was running well without encryption and all code was done accordingly.
  • Once action method start accepting encrypted query string it must not accept plain query string. i.e. without encryption. For e.g. earlier http://localhost/Invoice/ViewInvoice?invoiceid=123 after encryption it could be http://localhost/Invoice/ViewInvoice/ENCRYPTED-QUERY-STRING now it must not serve http://localhost/Invoice/ViewInvoice?invoiceid=123 (this could be possible due to default Value Provider)
  • In one sentence, I don’t want to change anything in action method implementation not even decryption nor change in action parameter and model binding must take care of this decryption wherever applicable (read only wherever applicable).

So I created one Value Provider which handle this decryption logic. For those who don’t know what is Value Provider in ASP.NET MVC.

In one sentence, Value Provider feeds data to model binder.

using System.Web.Mvc;
public class CryptoValueProvider : IValueProvider
{
    public bool ContainsPrefix(string prefix)
    {    }
 
    public ValueProviderResult GetValue(string key)
    {    }
}
 
public class CryptoValueProviderFactory : ValueProviderFactory
{
    public override IValueProvider GetValueProvider(ControllerContext controllerContext)
    {
        return new CryptoValueProvider();
    }
}

And registered this value provider in global.asax.cs as follow.

protected void Application_Start()
{
    ValueProviderFactories.Factories.Insert(0, new CryptoValueProviderFactory());
}

Here we added CryptoValueProvide at 0th index because we wanted to give it utmost priority and it started working as we expected model binder took place after decrypting (if it is encrypted) query string. Everything was agreed except point 3 mentioned above. Because now whenever user enter http://localhost/Invoice/ViewInvoice?invoiceid=123 manually at that time CryptoValueProvider does not find valid encrypted query string hence model binder passes model binding request to next ValueProvider i.e. System.Web.Mvc.QueryStringValueProvider (there are total 5 default value provider) and it found valid query string parameter and model binder took place! As per the point 3, this must not allow.

In this case, we need to ensure that for encrypted query string, there must be only one ValueProvider and that is CryptoValueProvider and default value provider must not act in the case of encrypted query string. In other word, we need to change (and not add/insert in) value provider collection run time instead registering it globally via global.asax.cs.

Again this is the point where you should be aware with ASP.NET MVC life cycle to decide where to alter value provider collection. Covering complete life cycle is beyond the scope of this post. I will try to cover in upcoming post but for this post we only require to know that model binding take place after authorization. So we created CryptoValueProviderAttribute action filter which implements IAuthorizationFilter interface and here in this action filter we can alter value provider collection and we can decorate respective action method (which require encrypted query string) with this attribute. So now with combination of CryptoValueProvider and CryptoValueProviderAttribute we can ensure that the only CryptoValueProvider (and not default 5 value provider) will act in case of encrypted query string and vice-versa.

Conclusion

The goal of this post is to demonstrate robust extensibility feature of ASP.NET MVC framework with which we can implement our custom logic out of the box way (Yes of course with lesser code and ease of maintainability). Hope this would be helpful. Comments are welcome and stay connected on twitter.

Crypto class which handle encryption and decryption

public static class Crypto
{
    public static string Encrypt(Dictionary<string, string> keyValue)
    {
        // encrypt query string key value pair
    }
 
    public static Dictionary<string, string> Decrypt(string encryptedText)
    {
        // decrypt encrypted query string into key value pair
    }
}

Update
For detailed post on how to encrypt/decrypt string/dictionary read this post.

CryptoValueProvider

public class CryptoValueProvider : IValueProvider
{
    RouteData routeData = null;
    Dictionary<string, string> dictionary = null;
 
    public CryptoValueProvider(RouteData routeData)
    {
        this.routeData = routeData;
    }
 
    public bool ContainsPrefix(string prefix)
    {
        if (this.routeData.Values["id"] == null)
        {
            return false;
        }
 
        this.dictionary = Crypto.Decrypt(this.routeData.Values["id"].ToString());
 
        return this.dictionary.ContainsKey(prefix.ToUpper());
    }
 
    public ValueProviderResult GetValue(string key)
    {
        ValueProviderResult result;
        result = new ValueProviderResult(this.dictionary[key.ToUpper()], 
            this.dictionary[key.ToUpper()], CultureInfo.CurrentCulture);
        return result;
    }
}

CryptoValueProviderAttribute

public class CryptoValueProviderAttribute : FilterAttribute, IAuthorizationFilter
{
    public void OnAuthorization(AuthorizationContext filterContext)
    {
        filterContext.Controller.ValueProvider = new CryptoValueProvider(filterContext.RouteData);
    }
}

Usage

[CryptoValueProvider]
public ActionResult ViewInvoice(int invoiceid)
{
    return View();
}

You can follow me on twitter for latest link and update on ASP.NET & MVC.

comments powered by Disqus

Featured Content

Resources & Tools

About Nandip Makwana

Nandip Makwana is passionate about digital world and web. He completed his Masters in Computer Application in June 2011. Currently he is working as a Software Engineer. He has shown great promise and command over ASP.NET and technologies surrounding it during his academic years and professorial life...continue reading