ASP.NET MVC – Unit Testing JsonResult Returning Anonymous Types

by ashic 25. May 2010 15:41

Unit testing of ASP.NET MVC JsonResults can be a source of confusion. The problem arises from the fact that an Action Method itself doesn't produce any html / json / string output – it simply returns an Action Result. ASP.NET MVC then calls the ExecuteResult() method on that Action Result. The ExecuteResult() method is what causes output to be written to the Response stream. Let's take the following controller and action method for example:



public class HomeController : Controller
{
    public ActionResult Index()
    {
        var people = new List<Person>
        {
             new Person{ Name = "Adam", Age=21},
             new Person{ Name = "Eve", Age=20},
             new Person{ Name = "Joe", Age=50}
        }; 

        return Json(new
        {
            People = people,
            Count = people.Count,
            Success = true
        }, JsonRequestBehavior.AllowGet);
    }
}

Here's the (very simple) Person class:



public class Person
{
    public string Name { get; set; }
    public int Age { get; set; }
}

As you can see, the Index method is simply returning an anonymous type which holds a list of people, the count, indicates success and wraps the whole thing in a JsonResult. When the Index() action method executes (i.e. is called from the browser), it produces the following json:

{"People":[{"Name":"Adam","Age":21},{"Name":"Eve","Age":20},{"Name":"Joe","Age":50}],"Count":3,"Success":true}

Nothing new there. Now let's say we wish to unit test this method [I know, I know…in a real app, the list of people would come from a service or repository or whatever…but for unit tests, you'd have mock whatevers or stub whatevers and would know what the data being returned would be]. We naively write something like this:



[TestMethod]
public void IndexTestWhichShouldFail()
{
    //arrange
    HomeController controller = new HomeController();

    //act
    var result = controller.Index() as JsonResult;

    //assert
    Assert.AreEqual(@"{""People"":[{""Name"":""Adam"",""Age"":21},{""Name"":""Eve"",""Age"":20},{""Name"":""Joe"",""Age"":50}],""Count"":3,""Success"":true}",
        result.Data.ToString());
}

We run the test, and it fails miserably:

Assert.AreEqual failed. Expected:<{"People":[{"Name":"Adam","Age":21},{"Name":"Eve","Age":20},{"Name":"Joe","Age":50}],"Count":3,"Success":true}>. Actual:<{ People = System.Collections.Generic.List`1[MvcApplication1.Models.Person], Count = 3, Success = True }>.

What went wrong? The reason that the test failed was because we were comparing apples to oranges. We expected a json string of results, but we were passing the ToString() value of the JsonResult's Data property. But shouldn't Data.ToString() simply give us the json string? Erm…no. The Data property simply holds the object passed into the Json() method in Index(). As such, it's simply an instance of an anonymous type. Calling ToString() on it does not cause serialization to json…it simply tells .NET to convert it to a string. The Actual value of the test result above shows what that looks like (i.e. instead of the People list getting serialzed to json, we get an ugly List`1 of Person). So how come it gives us proper json when running from a browser? The reason is that ASP.NET MVC takes the JsonResult and calls its ExecuteResult() method, which (among other things) writes the json to the Response.

So how do we unit test that?

Well, if we were returning non-anonymous types, then we could simply cast JsonResult.Data to the expected type and check its properties. But we're returning an anonymous type, aren't we?

So how do we unit test that?

Well, there're three approaches we can take.

Approach 1: The Easy Way

We know that we have the object and we know what the json for it should be. We can just serialize the object and see if it matches what we expect. Here's the test:



[TestMethod()]
public void IndexTest()
{
    //arrange
    HomeController controller = new HomeController(); 
   
    //act
    var result = controller.Index() as JsonResult;
    var serializer = new JavaScriptSerializer();
    var output = serializer.Serialize(result.Data);

    //assert
    Assert.AreEqual(@"{""People"":[{""Name"":""Adam"",""Age"":21},{""Name"":""Eve"",""Age"":20},{""Name"":""Joe"",""Age"":50}],""Count"":3,""Success"":true}",
        output);
}

Nothing complicated there. It's basically doing the same thing as the ExecuteResult() method of JsonResult does (in terms of producing the Json string via the JavaScriptSerializer). Now some of you may argue that this is not a unit test as it tests serialization as well as the method under test (i.e. Index). I would disagree. The reason being that you should only be testing your code. Testing JavaScriptSerializer should not be your focus when you're testing the Index method. And since checking the serialzed result is easier for you (i.e. you know what the json should be), you may as well check against that.

Approach 2: The "Pure" Way

This approach executes pretty much what the MVC framework does to produce output. For this, we're going to use Moq to fake a controller context and response and capture the content written to Response during execution in a StringBuilder. We will execute the result with the fake context, which will fill our StringBuilder with the serialized output. We will then check to see if it is what we want it to be. Here's the test:



[TestMethod()]
public void IndexTestWithMock()
{
    //arrange
    HomeController controller = new HomeController();
    var result = controller.Index();
    var sb = new StringBuilder();
    Mock<HttpResponseBase> response = new Mock<HttpResponseBase>();
    response.Setup(x => x.Write(It.IsAny<string>())).Callback<string>(y =>
    {
        sb.Append(y);
    });
    Mock<ControllerContext> controllerContext = new Mock<ControllerContext>();
    controllerContext.Setup(x => x.HttpContext.Response).Returns(response.Object);

    //act
    result.ExecuteResult(controllerContext.Object);

    //assert
    Assert.AreEqual(@"{""People"":[{""Name"":""Adam"",""Age"":21},{""Name"":""Eve"",""Age"":20},{""Name"":""Joe"",""Age"":50}],""Count"":3,""Success"":true}",
        sb.ToString());
}

Notice that our "act" of the test is not when we call controller.Index(), rather when we call ExecuteResult() on the JsonResult. In the previous approach, we simply called into the controller's action method. In this approach, we are actually doing what MVC does when dealing with a Request (at least, parts of it). To make it play the way we want it to, we created Mocks that behave the way we want them to. [Of course, the "arrange", "act" and "assert" parts are just nomenclature and one could say that the "act" is when Index() is called and everything afterwards is part of "assert"…]

Approach 3: Reflection

The previous two approaches compared the generated json against the expected json. As I said, that might upset some people as it kind of tests serialization too. I'm fine with it, but if someone wanted to test the JsonResult itself and not the generated output, one approach they can take is reflection. Here's a test that does just that:



[TestMethod]
public void IndexTestWithReflection()
{
    //arrange
    HomeController controller = new HomeController();

    //act
    var result = controller.Index() as JsonResult;

    //assert
    var data = result.Data;
    var type = data.GetType();
    var countPropertyInfo = type.GetProperty("Count");
    var expectedCount = countPropertyInfo.GetValue(data, null);

    Assert.AreEqual(3, expectedCount);
}

Since result.Data is an instance of an anonymous type, you can't really cast it to anything useful. You can check the values of result.Data via reflection. It can be a bit messy, but you could write a utility library to get to the properties in an easier fashion. There is one problem though…you're checking for strings (i.e. the property "Count") and as such, refactorings like renaming of a property will cause your tests to fail.

Approach 4: Dynamic

One way to avoid reflection would be to use .Net 4.0's dynamic capabilities. This does, however, require a bit of tinkering. We'll need to open up AssemblyInfo.cs of the MVC application and add this to the bottom:


[assembly:InternalsVisibleTo("TestProject1")]

Why we need to do this is explained in this article.

Once that's done, we can write our test like this:



[TestMethod]
public void IndexTestWithDynamic()
{
    //arrange
    HomeController controller = new HomeController();    

    //act
    var result = controller.Index() as JsonResult;    

    //assert
    dynamic data = result.Data; 

    Assert.AreEqual(3, data.Count);
    Assert.IsTrue(data.Success);
    Assert.AreEqual("Adam", data.People[0].Name);
}

Note how we're avoiding reflection, but still are testing the data that went into the JsonResult as opposed to the json string gotten from executing the JsonResult.


All three of these approaches will work. Pick whichever one suits your ideologies.

And yes, I'm aware that the Index() method would be better off returning a PagedList<Person>, and that Success=true is for the most part redundant (if there was an error, it should throw an exception and the calling javascript code should be able to understand that there was an error). The Index() method is for illustrative purposes only ;)

Update

I've added a fourth approach (i.e. dynamic). The source code download below contains the update.

Code

Click here to download the source code.

Shout it kick it on DotNetKicks.com

---------------------------------------------------------

Questions  and comments relating to this article are welcome. Comments completely unrelated to the article and posted with the sole intention of putting your link here are not.

If you spam, your comment will not be approved, will be deleted and your IP blocked. I maintain my site almost daily and such comments – even if they pass the spam filter – will get removed as soon as possible. If this gets too tedious, I may disable comments entirely. Please don't ruin it for everybody else.

---------------------------------------------------------

Share or Bookmark this post…
  • Facebook
  • DotNetKicks
  • Digg
  • LinkedIn
  • Technorati
  • del.icio.us
  • Google
  • Live
  • Tumblr
  • msdn Social
  • Ping.fm
  • Reddit
  • Slashdot
  • StumbleUpon
  • TwitThis
Categories: .NET | ASP.NET | ASP.NET MVC

ASP.NET MVC: ModelBinding Multiple File Uploads to an Array

by ashic 6. May 2010 14:46

Web apps often need to upload files. ModelBinding a file upload to an HttpPostedFileBase action parameter is straightforward as long as you know the exact name of the file upload html control. There may be some situations where there are quite a few file upload controls and for whatever reason, they have very different names (they could be autogenerated on the page for example). In such a case, it becomes difficult to know what names to give to the action parameters at compile time. The usual option would be to hook into the Request parameters. That works, but is not very testable (without a good mocking framework like SharpMock, Moles, TypeMock Isolator etc.). In such situations, we can use a model binder to bind all file uploads to a single upload parameter.

The Container

The first thing to do is to create the class to hold the information relating to the uploaded files:



public class UploadedFileInfo
{
    public string Name { get; private set; }
    public HttpPostedFileBase File { get; private set; }

    public UploadedFileInfo(string name, HttpPostedFileBase file)
    {
        Name = name;
        File = file;
    }  
}

The class is pretty simple. It holds the name of the file upload control that uploaded a file and the uploaded file itself.

The ModelBinder

Next comes the ModelBinder:



public class UploadedFileInfoArrayBinder : IModelBinder
{
    public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
    {
        var files = controllerContext.HttpContext.Request.Files;
        var list = new List<UploadedFileInfo>();

        for (int i = 0; i < files.Count; i++)
        {
            var file = files[i];
            var name = files.AllKeys[i]; 
            var fileInfo = new UploadedFileInfo(name, file); 
            list.Add(fileInfo);
        }

        return list.ToArray();
    }
}

Basically, the binder looks into Request.Files and for each file present, it creates and instance of UploadedFileInfo and adds it to a list. It simply returns the list as an array. Please note that if a user does not select a file for an upload control, it will still have an entry in Request.Files, only the uploaded file's ContentLength would be zero.

The Controller

And the controller:



public class UploadTestController : Controller
{
    [HttpGet]
    public ActionResult Index()
    {
        return View();
    }

    [HttpPost]
    public ActionResult Index(UploadedFileInfo[] files)
    {
        var uploadedCount = files.Where(x => x.File.ContentLength > 0).Count();
        ViewData["Message"] = string.Format("{0} file(s) uploaded successfully", uploadedCount);

        return View();
    }
}

The GET Index() is trivial. The Post Index() fetches the number of files in the model-bound files array that have a non-zero content length and passes the info to the View.

The View

And the view:


<body>
    <div>
        <form enctype="multipart/form-data" method="post">
            <input type='file' name='file1' id='file1' />
            <input type='file' name='file2' id='file2' />
            <input type="submit" value='upload' />
        </form>
    </div>
    <br />
    <%: ViewData["Message"] %>
</body>

The View is the same for the GET and the POST (don't mind the fact that I should be redirecting after the post – this is a demo after all!). There's a form that has two file uploads in a form. The file uploads have different names. The View also spits ViewData["Message"] if present.

Assigning the ModelBinder in Global.asax.cs

At this point, our code still won't work as the ModelBinder is not being applied to the files parameter of the POST Index(). We can add an attribute to the parameter, but there's a cleaner, nicer way to set it up in the global.asax.cs file:



protected void Application_Start()
{
    AreaRegistration.RegisterAllAreas();
    RegisterRoutes(RouteTable.Routes);
    ModelBinders.Binders[typeof(UploadedFileInfo[])] = new UploadedFileInfoArrayBinder();
}

In the last line, we're basically telling MVC that whenever it encounters a parameter to an ActionMethod that is an array of UploadedFileInfo, it should use an UploadedFileInfoArrayBinder. The binder will then fetch the files from the Request and assign it to the parameter.

Running the App

With all this in place, we can run the app, select a couple of files and hitting upload should tell us that the files have been uploaded.

Source

download

---------------------------------------------------------

Questions  and comments relating to this article are welcome. Comments completely unrelated to the article and posted with the sole intention of putting your link here are not.

If you spam, your comment will not be approved, will be deleted and your IP blocked. I maintain my site almost daily and such comments – even if they pass the spam filter – will get removed as soon as possible. If this gets too tedious, I may disable comments entirely. Please don't ruin it for everybody else.

---------------------------------------------------------

Share or Bookmark this post…
  • Facebook
  • DotNetKicks
  • Digg
  • LinkedIn
  • Technorati
  • del.icio.us
  • Google
  • Live
  • Tumblr
  • msdn Social
  • Ping.fm
  • Reddit
  • Slashdot
  • StumbleUpon
  • TwitThis
Categories: ASP.NET | ASP.NET MVC

Encrypted Hidden Redux : Let's Get Salty

by ashic 13. March 2010 09:37

This article builds on the ideas presented in my earlier article http://www.heartysoft.com/post/2010/02/25/Encrypted-Hidden-Inputs-in-ASPNET-MVC.aspx

If you haven't read that yet, I'd recommend doing so before proceeding.

Problems with the Previous Approach

The approach outlined in the previous article is pretty secure and easy to use. However, there are a few issues that can be improved upon:

  1. Security: The approach is using a symmetric encryption process with a fixed private key. Asymmetric encryption would no doubt provide more security, but the performance costs and hassle is probably not worth it. The problem with symmetric encryption is that brute force attacks can potentially break it. As such, using only a fixed encryption key could be a security issue. Given enough time and test pages, the encrypted values could lead to the leaking of the private key. This would result in the whole process being compromised. A couple of people provided feedback on this and were concerned. Still, such brute force attacks could take hundreds of years as the attacker would be required to test at least 2^56 keys. That's not an easy task, but we can use some salting techniques to make it even harder (a lot harder) to break. This would still be within the realms of symmetric encryption but would provide (a LOT of) added security. I'll explain the salting technique later.
  2. CSRFs: With the previous approach, an attacker could take the encrypted value and submit it as part of another request from his browser. The system would decrypt and use the value as normal. The previous process expected that the developer would use ASP.NET MVC's AntiForgeryToken to prevent CSRFs. In other words, the process did not prevent CSRFs on its own, but expected the developer to handle them by other means. With the new process outlined in this article, this will no longer be necessary.
  3. Encryption and Hashing method: The previous process used Triple DES for encryption and MD5 for hashing. The new process uses Rijndael encryption and SHA256 hashing. Rijndael is approved by the US government and is natively supported by the .Net framework. If the encryption and decryption are both going to be done on Windows boxes, then Rijndael is definitely the encryption algo to use over TDES. Similarly. SHA256 has a managed implementation and is superior to MD5.
  4. Code Smell: The previous implementation kind of had some code smells. Stuff was there where it shouldn't have been and it just didn't feel right. I reworked the design and although all smells aren't gone (hey, it's a demo!), a lot of the stench has been removed.

So What's Our Salt?

A salt is basically some random bytes added into our actual data during the encryption process. Having access to the key and the salt enables complete decryption. Having access to one but not the other doesn't. The salt needs to be random enough so that different users have different ones. We still need the salt to facilitate decryption on the server. A common technique is to store part of the salt at the server and part of it in the html sent to the browser. Other techniques generate the salts using some logic and sends some or none of the salt to the client. The bottom line is that the salt needs to be random so that an attacker can't simply take some values from a legitimate page output and then post it himself. The salt still needs to be available on the server when a legitimate user submits the page. ASP.NET MVC actually has such a per user per session value baked right in. It's the AntiForgeryToken. However there are a couple of issues that prevent us from using it directly:

1. The AntiForgeryToken helper uses some internal classes to generate the token data. As it's internal to MVC, we can't directly access the token data. We need to generate the AntiForgeryToken input tag and parse its value.

2. The AntiForgeryToken value is per user per session. If the developer puts an AntiForgeryToken on the page, then that value will be visible to the attacker. That essentially hands the attacker the salt. This is not acceptable. Luckily, the AntiForgeryToken helper can accept any string as its salt (yes, its own salt – not our final salt). We can pass a fixed string as the salt and that would ensure that using AntiForgeryTokens on the page doesn't interfere with or expose our salt.

As I said, the AntiForgeryToken's value is constant for the same user in the same browser session and the same "salt" parameter passed to the helper. But if the user opens another browser window or another user uses the system, then the value would be completely different. With this approach, we don't need to send any of the salt to the browser as all of it can be generated on the server on page submission. Only the encrypted values are ever sent to the client.

The Project

Since quite a bit of things have changed since the previous approach, I'll start afresh. First thing to do is create an MVC 2 project called "HiddenEncryptDemo".

I installed MVC 2 RC 2 before installing VS 2010 RC. If you have some other combination, then slight changes might be necessary, although the core ideas will still work (even with MVC 1.0).

Create a Models folder and add in a class called Computer:


public class Computer
{
    public string Setting { get; set; }
}

The reason I'm adding this class is to show that our approach will work not just on simple action parameters, but also on more complex types without a need for a separate model binder.

Next, add this to the Index.aspx view (in the /Views/Home folder):


<% Html.BeginForm("Something", "Home", FormMethod.Post); %>
    <%= Html.Hidden("computer.Setting", "hello world!") %>
    <input type="submit" value='submit' />
<% Html.EndForm(); %>

After that, add the following method to the Home controller:


[HttpPost]
public ActionResult Something(Computer computer)
{
    ViewData["Message"] = computer.Setting;
    return View("Index");
}

Nothing fancy, we're just setting the ViewData["Message"] to the computer.Setting value. Notice that in the code we added to Index.aspx, the form submitted to "Something" (the name of our action) and the name of the hidden input was "computer.Setting". The default model binder will find a value for "computer.Something" in the request parameters and upon seeing that it can set the Setting property of the computer parameter, it's going to set computer.Setting to the value it found (in our case, "hello world!"). If you run the project, you should see the index page with a submit button. Clicking the button will result in a post to the server where the Something action will get called. The Something action will then set the ViewData["Message"] and return the Index view. As such, you will see the words "hello world!" displayed on the page:

hello

On the initial Index page (or the page got after clicking the button), if you right click anywhere and select view source, you should see this (among other things):

hidden

Notice how the helper added a hidden input control, set its id to "computer_Setting" and name to "computer.Setting". The name is used as the key of the data in the post variables. Also notice how the value is "hello world!". The value is clearly visible to anyone looking at the source. Our goal is to come up with a way to hide that value from clear sight while at the same time ensuring everything works as smoothly and easily as it just did. A bonus would be if no change was required in the controller's code (i.e. no specific attributes needed on the consuming action or controller, no special model binding needed for the parameter etc.) – it would be unobtrusive. These are the exact requirements of the previous approach, but today, we want to add one more – the encryption should use a different salt for each user's session so that brute force approaches of uncovering the encryption key will fail and CSRFs will be prevented by default.

The Settings Provider

In the previous article, we handled settings from within the encryption provider. This seemed kind of messy and I decided to use a separate settings provider. Add a "Helpers" folder to the project and add the interface ISettingsProvider to it:


public interface ISettingsProvider
{
    byte[] EncryptionKey { get; }
    string EncryptionPrefix { get; }
    string SaltGeneratorKey { get; }
}

Next, add an implementation for the interface. The SettingsProvider class looks like this:


namespace HiddenEncryptDemo.Helpers
{
    using System.Configuration;
    using System.Security.Cryptography;
    using System.Text;
   
    public class SettingsProvider : ISettingsProvider
    {
        private static readonly byte[] _encryptionKey;
        private static readonly string _encryptionPrefix;
        private static readonly string _saltGeneratorKey;

        static SettingsProvider()
        {
            //read settings from configuration
            var useHashingString = ConfigurationManager.AppSettings["UseHashingForEncryption"];
            bool useHashing = true;
            if (string.Compare(useHashingString, "false", true) == 0)
            {
                useHashing = false;
            }

            _encryptionPrefix = ConfigurationManager.AppSettings["EncryptionPrefix"];
            if (string.IsNullOrWhiteSpace(_encryptionPrefix))
            {
                _encryptionPrefix = "encryptedHidden_";
            }

            _saltGeneratorKey = ConfigurationManager.AppSettings["EncryptionSaltGeneratorKey"];
            if (string.IsNullOrWhiteSpace(_saltGeneratorKey))
            {
                _saltGeneratorKey = "encryptionSaltKey";
            }

            var key = ConfigurationManager.AppSettings["EncryptionKey"];

            if (useHashing)
            {
                var hash = new SHA256Managed();
                _encryptionKey = hash.ComputeHash(UTF8Encoding.UTF8.GetBytes(key));
                hash.Clear();
                hash.Dispose();
            }
            else
            {
                _encryptionKey = UTF8Encoding.UTF8.GetBytes(key);
            }
        }

        #region ISettingsProvider Members

        public byte[] EncryptionKey
        {
            get { return _encryptionKey; }
        }

        public string EncryptionPrefix
        {
            get { return _encryptionPrefix; }
        }

        public string SaltGeneratorKey
        {
            get { return _saltGeneratorKey; }
        }

        #endregion
    }
}

Basically, we're just reading in some settings from the web.config file in the static constructor and returning the static values from the instance based getters. Notice that if UseHashingForEncryption setting is set, we first hash the encryption key in web.config using SHA256Managed before using it. The EncryptionPrefix is a prefix used to identify hidden inputs which were previously encrypted. The SaltGeneratorKey is a key used in getting the AntiForgeryToken.

The Encryption Provider

Add an interface called IEncryptString:


public interface IEncryptString : IDisposable
{
    string Encrypt(string value);
    string Decrypt(string value);
}

And an implementation called RijndaelStringEncrypter:


namespace HiddenEncryptDemo.Helpers
{
    using System;
    using System.Security.Cryptography;
    using System.Text;

    public class RijndaelStringEncrypter : IEncryptString
    {
        private RijndaelManaged _encryptionProvider;
        private ICryptoTransform _encrypter;
        private ICryptoTransform _decrypter;
        private byte[] _key;
        private byte[] _iv;

        public RijndaelStringEncrypter(ISettingsProvider settings, string salt)
        {
            _encryptionProvider = new RijndaelManaged();
            var saltBytes = UTF8Encoding.UTF8.GetBytes(salt);
            var derivedbytes = new Rfc2898DeriveBytes(settings.EncryptionKey, saltBytes, 3);
            _key = derivedbytes.GetBytes(_encryptionProvider.KeySize / 8);
            _iv = derivedbytes.GetBytes(_encryptionProvider.BlockSize / 8);
        }

        #region IEncryptString Members

        public string Encrypt(string value)
        {
            var valueBytes = UTF8Encoding.UTF8.GetBytes(value);

            if (_encrypter == null)
            {
                _encrypter = _encryptionProvider.CreateEncryptor(_key, _iv);
            }

            var encryptedBytes = _encrypter.TransformFinalBlock(valueBytes, 0, valueBytes.Length);
            var encrypted = Convert.ToBase64String(encryptedBytes);

            return encrypted;
        }

        public string Decrypt(string value)
        {
            var valueBytes = Convert.FromBase64String(value);

            if (_decrypter == null)
            {
                _decrypter = _encryptionProvider.CreateDecryptor(_key, _iv);
            }

            var decryptedBytes = _decrypter.TransformFinalBlock(valueBytes, 0, valueBytes.Length);
            var decrypted = UTF8Encoding.UTF8.GetString(decryptedBytes);

            return decrypted;
        }

        #endregion

        #region IDisposable Members

        public void Dispose()
        {
            if (_encrypter != null)
            {
                _encrypter.Dispose();
                _encrypter = null;
            }

            if (_decrypter != null)
            {
                _decrypter.Dispose();
                _decrypter = null;
            }

            if (_encryptionProvider != null)
            {
                _encryptionProvider.Clear();
                _encryptionProvider.Dispose();
                _encryptionProvider = null;
            }
        }

        #endregion
    }
}

This is a sginificant change from our previous encryption method. Previously, we created a single pair of ICryptoTransforms for encryption and decryption based on the encryption key. Since we need to provide different salts for encryption for different users and different sessions, that approach will no longer work. In the new approach, each instance of the RijndaelStringEncrypter has its own salt value. As a consequence, each instance needs its own pair of ICryptoTransforms. Looking at the constructor code:


public RijndaelStringEncrypter(ISettingsProvider settings, string salt)
{
    _encryptionProvider = new RijndaelManaged();
    var saltBytes = UTF8Encoding.UTF8.GetBytes(salt);
    var derivedbytes = new Rfc2898DeriveBytes(settings.EncryptionKey, saltBytes, 3);
    _key = derivedbytes.GetBytes(_encryptionProvider.KeySize / 8);
    _iv = derivedbytes.GetBytes(_encryptionProvider.BlockSize / 8);
}

we see that we're storing the key and iv bytes as instance variables. Please note that simply getting the bytes for the salt string using UTF8Encoding.UTF8.GetBytes(salt) is not good enough. The values generated by AntiForgeryToken - while different for each user and session – can be very similar. As such, if you're encrypting a small word (for example "hello") and use the encoding derived bytes, the first few bytes may be so similar that the encrypted output of "hello" might appear the same in the html output for different users / sessions. As such, we're using Rfc2898DerivedBytes to ensure that only the exact salt and encryption key produce the same iv bytes. Another thing to notice is that we're using the encryption key itself as the password passed to the Rfc2898DerivedBytes constructor. This essentially means that the encryption key and the passed in salt string is used to derive the actual iv (salt) used for encryption.

The Html Helper

Add a file called InputExtensions.cs and add the following code to it:


namespace HiddenEncryptDemo.Helpers
{
    using System.Web.Mvc;
    using System.Web.Mvc.Html;
    using System.Text.RegularExpressions;

    public static class InputExtensions
    {
        public static HeartysoftHtmlHelper Heartysoft(this HtmlHelper helper)
        {
            return new HeartysoftHtmlHelper(helper);
        }
    }

    public partial class HeartysoftHtmlHelper
    {
        private readonly HtmlHelper _helper;
        private readonly ISettingsProvider _settings;

        private static Regex _valueExtractorRegex = new Regex(".*value=\"(.+)\"/*", RegexOptions.Compiled);

        public HeartysoftHtmlHelper(HtmlHelper helper)
            : this(helper, new SettingsProvider())
        {
        }

        public HeartysoftHtmlHelper(HtmlHelper helper, ISettingsProvider settings)
        {
            _helper = helper;
            _settings = settings;
        }

        public string GetAntiForgeryToken(string salt)
        {
            var input = _helper.AntiForgeryToken(salt).ToString();
            var match = _valueExtractorRegex.Match(input);
            return match.Groups[1].Value;
        }

        public MvcHtmlString EncryptedHidden(string name, object value)
        {
            if (value == null)
            {
                value = string.Empty;
            }

            var strValue = value.ToString();
            string salt = GetAntiForgeryToken(_settings.SaltGeneratorKey);

            var encrypter = new RijndaelStringEncrypter(_settings, salt);
            var encryptedValue = encrypter.Encrypt(strValue);
            encrypter.Dispose();

            var encodedValue = _helper.Encode(encryptedValue);
            var newName = string.Concat(_settings.EncryptionPrefix, name);

            return _helper.Hidden(newName, encodedValue);
        }
    }
}

Let's dissect the helper a bit. There are basically two important methods. The first is GetAntiForgeryToken:


public string GetAntiForgeryToken(string salt)
{
    var input = _helper.AntiForgeryToken(salt).ToString();
    var match = _valueExtractorRegex.Match(input);
    return match.Groups[1].Value;
}

This get's the anti forgery token string given a specific salt. As I mentioned before, the AntiForgeryToken helper gets its value from a class internal to ASP.NET MVC and thus we can't get the value directly. To work around this, I'm using the helper to het the html of the anti forgery token hidden input and using a Regex to parse its value. The other interesting method is the EncryptedHidden function:


public MvcHtmlString EncryptedHidden(string name, object value)
{
    if (value == null)
    {
        value = string.Empty;
    }

    var strValue = value.ToString();
    string salt = GetAntiForgeryToken(_settings.SaltGeneratorKey);

    var encrypter = new RijndaelStringEncrypter(_settings, salt);
    var encryptedValue = encrypter.Encrypt(strValue);
    encrypter.Dispose();

    var encodedValue = _helper.Encode(encryptedValue);
    var newName = string.Concat(_settings.EncryptionPrefix, name);

    return _helper.Hidden(newName, encodedValue);
}

This method uses the previous one to get the salt, creates the encrypter and encrypts the input. It then uses the regular Hidden() helper to return the <input> tag with the value set to the encrypted data.

Configuration

Go to web.config and ensure that under <configuration>, the following entries are present:


<appSettings>
    <add key="EncryptionKey" value="asdjahsdkhaksj dkashdkhak sdhkahsdkha kjsdhkasd"/>
</appSettings>

The View

At this point, go to Index.aspx and change the html of the form to:


<% Html.BeginForm("Something", "Home", FormMethod.Post); %>
    <%= Html.Heartysoft().EncryptedHidden("computer.Setting", "hello world!") %>
    <%--<%= Html.Hidden("computer.Setting", "hello world!") %>--%>
    <input type="submit" value='submit' />
<% Html.EndForm(); %>

I've just commented out the old helper and used our new helper. If you run the page and view the source, you should see this:


<input id="encryptedHidden_computer_Setting" name="encryptedHidden_computer.Setting" type="hidden" value="GlafeVH/jrfim7gXfqhSHg==" />

If you refresh the page and view source, you should see this:


<input id="encryptedHidden_computer_Setting" name="encryptedHidden_computer.Setting" type="hidden" value="GlafeVH/jrfim7gXfqhSHg==" />

i.e. the value of the hidden input has not changed. If you open the same page in a different browser or close and open the browser or open the same page in a new window (not tab as sessions are shared across tabs), then you should see this:


<input id="encryptedHidden_computer_Setting" name="encryptedHidden_computer.Setting" type="hidden" value="IM94hlaZP4HmqOhxw7Ewyg==" />

Notice how the value is different from the previous value. If you kept the previous browser open, then refreshing the page and viewing source, you should see this:


<input id="encryptedHidden_computer_Setting" name="encryptedHidden_computer.Setting" type="hidden" value="GlafeVH/jrfim7gXfqhSHg==" />

In other words, each session is getting a unique value that's different from the value got in a different session. As such, we can say that the encryption is using a per session salt which makes brute force attacks that much harder.

Please note that since the salt is per session, the exact values of the hidden inputs will vary greatly from the ones shown here. What's important is that in the same session, the value is the same, but the value differs from session to session.

The Controller Factory

We now need to actually decrypt values when the page is posted back. We do this via a custom ControllerFactory. Our controller factory will look for any encrypted input parameters, decrypt them and add them to RouteData using their original names so that they're available to the model binder. Add a class called DecryptingControllerFactory:


namespace HiddenEncryptDemo.Helpers
{
    using System.Linq;
    using System.Web.Mvc;
    using System.IO; 
   
    public class DecryptingControllerFactory : DefaultControllerFactory
    {
        private ISettingsProvider _settings = new SettingsProvider();

        public override IController CreateController(System.Web.Routing.RequestContext requestContext, string controllerName)
        {
            var parameters = requestContext.HttpContext.Request.Params;
            var encryptedParamKeys = parameters.AllKeys.Where(x => x.StartsWith(_settings.EncryptionPrefix)).ToList(); 

            IEncryptString decrypter = null;

            foreach (var key in encryptedParamKeys)
            {
                if (decrypter == null)
                {
                    decrypter = GetDecrypter(requestContext);
                }

                var oldKey = key.Replace(_settings.EncryptionPrefix, string.Empty);
                var oldValue = decrypter.Decrypt(parameters[key]);
                requestContext.RouteData.Values[oldKey] =  oldValue;
            }

            if (decrypter != null)
            {
                decrypter.Dispose();
            }

            return base.CreateController(requestContext, controllerName);
        }

        private IEncryptString GetDecrypter(System.Web.Routing.RequestContext requestContext)
        {
            var salt = GetCurrentSalt(requestContext);
            var decrypter = new RijndaelStringEncrypter(_settings, salt);
            return decrypter;
        }

        private string GetCurrentSalt(System.Web.Routing.RequestContext requestContext)
        {
            var controllerContext = new ControllerContext();
            controllerContext.RequestContext = requestContext;
            var viewContext = new ViewContext(controllerContext, new WebFormView("dummy"),
                new ViewDataDictionary(), new TempDataDictionary(), TextWriter.Null);

            var helper = new HtmlHelper(viewContext, new ViewPage());
            var afToken = helper.Heartysoft().GetAntiForgeryToken(_settings.SaltGeneratorKey);
            return afToken;
        }
    }
}

The controller factory is quite simple. It first checks the request parameters to see if there are any that have a key with a prefix equal to the EncryptionPrefix provided by the settings provider. If there are, we cycle through each and add an entry into RouteData with the key set to the parameter's key minus the prefix and the value set to the decrypted value. We create the decrypter only if needed so that if there aren't any encrypted hidden inputs, we don't waste any resources. Furthermore, if we do need a decrypter, we create an instance and reuse it for all the encrypted parameters for the request. Since the salt is per session, reusing the decrypter for all the encrypted parameters in the request is safe and reduces resource usage. To create the decrypter, we need the salt value that has to be equal to the salt used for encrypting. This is where the very interesting GetCurrentSalt method comes in:


private string GetCurrentSalt(System.Web.Routing.RequestContext requestContext)
{
    var controllerContext = new ControllerContext();
    controllerContext.RequestContext = requestContext;
    var viewContext = new ViewContext(controllerContext, new WebFormView("dummy"),
        new ViewDataDictionary(), new TempDataDictionary(), TextWriter.Null);

    var helper = new HtmlHelper(viewContext, new ViewPage());
    var afToken = helper.Heartysoft().GetAntiForgeryToken(_settings.SaltGeneratorKey);
    return afToken;
}

What this method is doing is it's faking a view context and controller context, setting the controller context's request context to that of the current request and using the fake view context and controller context to create a new instance of the HtmlHelper class. Then, it calls our previously coded GetAntiForgeryToken helper to get the value of the salt string. This is a very useful technique of creating an instance of the HtmlHelper class and can be used to create helpers outside of view code. Over at the ASP.NET forums, Brad Wilson expressed concerns that passing in TextWriter.Null may cause problems, but I believe this is not so. TextWriter.Null provides a TextWriter instance that can be written to but not read from. I don't think any Html helpers should be reading from the TextWriter – they should only be writing to it. And even if there are such helpers, AntiForgeryToken() and our GetAntiForgeryToken() do not read from the TextWriter, so this is perfectly safe for our purposes.

The end result of all of this is that if there are encrypted hidden inputs, then they are decrypted and the entries with original names (i.e. wothout the prefix) are added to RouteData. This ensures the original entries are available for ModelBinding purposes. The last change you need to do is open up the global.asax.cs file and ensure the Application_Start looks like this:


protected void Application_Start()
{
    AreaRegistration.RegisterAllAreas();
    RegisterRoutes(RouteTable.Routes);
    ControllerBuilder.Current.SetControllerFactory(typeof(DecryptingControllerFactory));
}

Running the Page

We'll need to stop the dev server (if you're using IIS, simply restart it). We need to do this because we made changes to the global.asax.cs page. Right click the dev server's icon in your traybar and click stop:

stop-server

You can now simply hit ctrl + F5 in VS to run the page. Click the button and you should see this:

result

Run the page in another browser and click the button. You should see the same results. Viewing the source, you'll see that while pages processed in the same session always has the same encrypted value, a different session in a different browser produces a different encrypted value. Still, when either value is posted to the server, the decrypting controller factory correctly decrypts the values and we get our original value in RouteData. Neither the encryption key nor the encryption iv is ever passed to the client and thus the process is much more secure than our previous approach.

And that's all there is to enable an unobtrusive, easy to use, secure, reusable and salted hidden inputs in ASP.NET MVC. On top of that, it should perform better since we're using managed Rijndael encryption which does not call out to non-managed resources like TDES does.

Possible Questions

I would recommend you first read the "possible questions" section of the previous article. Other than those:

3. Is the data really secure?
The previous approach was pretty secure. As I mentioned, an attacker would need at least 2^56 attempts. This new approach makes it much much more harder for the attacker to succeed. So, it's even more secure. The previous approach didn't tackle CSRFs but relied on the developer using AntiForgeryToken on the pages and the associated ValidateAntiForgeryToken attribute on the controller action being posted to. This approach handles CSRFs itself.

8. How is the performance?
The performance is pretty good. The controller factory only creates a decrypter when needed and reuses it for decrypting all encrypted parameters. We do, however, need to create an encrypter for each call to Html.Heartyfoft().EncryptedHidden(). A way to negate this would be to use some sort of per request IOC container so that one instance is used for processing a whole request. In my opinion, helpers should not have state, but if you want, implementing that part should be easy.

Source Code

Download

kick it on DotNetKicks.com Shout it

---------------------------------------------------------

Questions  and comments relating to this article are welcome. Comments completely unrelated to the article and posted with the sole intention of putting your link here are not.

If you spam, your comment will not be approved, will be deleted and your IP blocked. I maintain my site almost daily and such comments – even if they pass the spam filter – will get removed as soon as possible. If this gets too tedious, I may disable comments entirely. Please don't ruin it for everybody else.

---------------------------------------------------------

Share or Bookmark this post…
  • Facebook
  • DotNetKicks
  • Digg
  • LinkedIn
  • Technorati
  • del.icio.us
  • Google
  • Live
  • Tumblr
  • msdn Social
  • Ping.fm
  • Reddit
  • Slashdot
  • StumbleUpon
  • TwitThis
Categories: .NET | ASP.NET MVC | ASP.NET | Security

MVC 2 RC 2 Templated Helper Bug and a Potential Solution

by ashic 1. March 2010 13:43

The Problem

ASP.NET MVC 2 introduces templated helpers. They're a convenient and type safe way to render model data. There is a potential problem in the way in which the system processes the lambda expression passed in to the helper. The problem causes overridden properties to be ignored and uses the base class' declaration of the property instead. This can have adverse reactions where the client side validation feature of ASP.NET MVC 2 is used.

An Example

Create a very simple BasePerson class:


public class BasePerson
{
    public virtual string FirstName { get; set; }
}

Create a Person class that derives from the BasePerson class, but adds some data annotations (of course, you could do anything here that you required):


public class Person : BasePerson
{
    [Required(ErrorMessage = "FirstName is required.")]
    public override string FirstName { get; set; }
}

Add a very simple controller:


public class HomeController : Controller
{
    public ActionResult Index()
    {
        var p = new Person();
        return View(p);
    }
}

Create the Index.htm view:


<%@ Page Language="C#" Inherits="System.Web.Mvc.ViewPage<MvcApplication1.Models.BasePerson>" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
    <title>Index</title>
    <script src="/Scripts/MicrosoftAjax.js" type="text/javascript"></script>
    <script src="/Scripts/MicrosoftMvcAjax.js" type="text/javascript"></script>
    <script src="/Scripts/MicrosoftMvcValidation.js" type="text/javascript"></script>
</head>
<body>
    <% Html.EnableClientValidation(); %>
    <% Html.BeginForm(); %>
    <div>
        <%= Html.TextBoxFor(x=>x.FirstName) %>       
        <%= Html.ValidationMessageFor(x=>x.FirstName) %>
        <input type='submit' value='submit' />
    </div>
    <% Html.EndForm(); %>
</body>
</html>

You could have made the ViewPage of type Person, instead of BasePerson, but the error would have still been present.

If you run the project now, the rendered html responsible for client side validation would look like this:


//<![CDATA[ if (!window.mvcClientValidationMetadata) { window.mvcClientValidationMetadata = []; } window.mvcClientValidationMetadata.push({"Fields":
[{"FieldName":"FirstName","ReplaceValidationMessageContents":true,
"ValidationMessageId":"form0_FirstName_validationMessage","ValidationRules":[]}],
"FormId":"form0","ReplaceValidationSummary":false}); //]]>

As you can see, the validation rules are empty and as such, there's no client side validation occurring. To see this, just click the submit button. The form will post back without any problems. That should not be happening if the FirstName property is empty.

The Reason

The cause of the bug is a single line of code in the method FromLambdaExpression in the file ModelMetaData.cs in the source code of MVC 2 RC 2. Here's the relevant portion of code:


MemberExpression memberExpression = (MemberExpression)expression.Body;
string propertyName = memberExpression.Member is PropertyInfo ? memberExpression.Member.Name : null;
Type containerType = memberExpression.Member.DeclaringType;

While this looks pretty normal, the third line is the culprit. The DeclaringType refers to the class that declares a member – it's not necessarily the type of the class that's in the Model of our ViewData. In our example, although we passed in a Person object as the model, the DeclaringType of FirstName will always be BasePerson. The consequence of using the DeclaringType member to get the containerType is that the attribute added to the overriden FirstName of the Person class is totally ignored. Any other attributes on an overridden property will also be ignored.

Note that if you were to use TextBoxFor(model=>model) or TextBox("FirstName"), then this would not be a problem until and unless
somethingFor(x=>x.FirstName)
is encountered.
Also, server side validation isn't affected by the bug as the bug appears to be centred on that specific third line of code.

A Possible Solution

The main reason for the problem is that FromLambdaExpression is not using the runtime type of the container. Using the runtime type can be achieved in a few lines of code. Let's first add a helper method:


private static ParameterExpression GetParameterExpression(Expression expression)
{
    while (expression.NodeType == ExpressionType.MemberAccess)
    {
        expression = ((MemberExpression)expression).Expression;
    }
    if (expression.NodeType == ExpressionType.Parameter)
    {
        return (ParameterExpression)expression;
    }
    return null;
}

I've taken the GetParameterExpression method from here:
http://geekswithblogs.net/EltonStoneman/archive/
2009/11/05/retrieving-nested-properties-from-lambda-expressions.aspx

Credits for that go to Elton Stoneman

The last thing to do is to add a bit of code to retrieve the runtime type of the container:


MemberExpression memberExpression = (MemberExpression) expression.Body;
string propertyName = memberExpression.Member is PropertyInfo ? memberExpression.Member.Name : null;
Type containerType = memberExpression.Member.DeclaringType;

bool isContainerValueType = typeof (TParameter).IsValueType;

if(isContainerValueType || (isContainerValueType == false && container != null))
{
    var parameter = GetParameterExpression(memberExpression.Expression);
    var lambda = Expression.Lambda(memberExpression.Expression, parameter);
    var compiled = lambda.Compile();
    var containerInstance = compiled.DynamicInvoke(container);
    containerType = containerInstance.GetType();
}

Running the page and viewing the source, we should see this:


//<![CDATA[ if (!window.mvcClientValidationMetadata) { window.mvcClientValidationMetadata = []; } window.mvcClientValidationMetadata.push({"Fields":
[{"FieldName":"FirstName","ReplaceValidationMessageContents":true,
"ValidationMessageId":"form0_FirstName_validationMessage",
"ValidationRules":[{"ErrorMessage":"FirstName is required.","ValidationParameters":{},
"ValidationType":"required"}]}],"FormId":"form0","ReplaceValidationSummary":false}); //]]>

As you can see, all the client validation info is now rendered.

Explanation

What we've done here isn't really complicated. We're basically taking the old expression (say x=>x.Dummy.Car.Name), getting a lambda delegate for all but the last part of the old expression (so x=>x.Dummy.Car). We're then invoking the delegate with viewData.Model (which is stored in the container variable by code earlier in the method). This gives us the instance of the entity holding the property to be displayed (so in this case, Model.Dummy.Car where the property to be displayed is Model.Dummy.Car.Name). After that, we call GetType() on the instance to get the runtime type. In our coded example, this gives Person and not BasePerson. As such, the validation script rendered honours the [Required] attribute on the overridden FirstName property of the Person class.

I've also added some checks to handle null values for the Model. This can happen if an invalid string is entered for a bool, for example. In these cases, we fall back to the expression derived type. Some unit tests fail without this check. Initially, I also checked to ensure that parameter was not null, but it seems that's not required as it's handled earlier (and all the unit tests pass without this check).

And yes, this method will work on complex types with deep nesting. Not just on direct members of the Model.

Possible Problem

I can think of one area where this method may cause problems. If you're encapsulating TempData into a viewmodel to be rendered, then this code will cause a read on the TempData, causing it to expire. As such it won't get rendered onto the page. Of course, if you didn't use this approach, then the TempData would vanish immediately after rendering to the page. So why would you want to use TempData for this in the first place? Use ViewModels and ViewData for rendering. Don't use TempData in the View.

Does It Break Anything

After making the changes to the MVC 2 RC 2 source code, all existing 2,281 unit tests of the ASP.NET MVC 2 RC 2 solution still pass. As such, I'm assuming nothing is getting broken.

Does It Solve Anything Else

I don't know. If there're other places that depend on FromLambdaExpression, this should fix them as well. Other than that, can't really say.

kick it on DotNetKicks.com  Shout it

---------------------------------------------------------

Questions  and comments relating to this article are welcome.
Comments completely unrelated to the article and posted with the sole intention of putting your link here are not.

If you spam, your comment will not be approved, will be deleted and your IP blocked. I maintain my site almost daily and such comments – even if they pass the spam filter – will get removed as soon as possible. If this gets too tedious, I may disable comments entirely. Please don't ruin it for everybody else.

---------------------------------------------------------

Share or Bookmark this post…
  • Facebook
  • DotNetKicks
  • Digg
  • LinkedIn
  • Technorati
  • del.icio.us
  • Google
  • Live
  • Tumblr
  • msdn Social
  • Ping.fm
  • Reddit
  • Slashdot
  • StumbleUpon
  • TwitThis
Categories: .NET | ASP.NET MVC | ASP.NET

Encrypted Hidden Inputs in ASP.NET MVC

by ashic 25. February 2010 02:27

I have written an update to this article that shows a better way of doing what is presented here. The core concepts are pretty much the same and I would request you to read this one before the improved version.

There are many situations where we need to persist some data during the user's session. There are a few options in achieving this:

1. Session: Using the Session object will persist the data in server resources. By default, they are persisted to server RAM. Using Session is very simple as to persist, you just go Session["key"] = "value, and to retrieve, var val = Session["key"] as [type]. There are a couple of problems in this approach, however. Firstly, it consumes server resources. If you anticipate large volumes of traffic, you can't store the data in the server's RAM cause it'll simply be too much of a resource hog. Second, if you're using a web farm setup where multiple physical machines (or virtual machines) are hosting your site, then data stored on one machine's RAM will not be available to the other machine. So, if requests from the same user go to different machines, complications are bound to arise. The workaround to this can be using a state server. That also needs setting it up and maintaining, load balancing etc. Furthermore, using a state server would result in a (however small) time delay where state information is persisted and retrieved during requests.

2. Cookies: Information can easily be persisted by using cookies. You can't rely on them though, as many users have them disabled – and they're very easy to disable.

3. Hidden Inputs: Using this method, you store the data as the value of hidden input controls. These values get posted during form submission and you can retrieve them easily. The data is stored in the html sent to the browser and thus does not consume any server resources. There's a slight amount of work required to store the value and retrieve it, but other than that, it's an ideal way to persist session specific data in a scalable way. We are going to explore this method in this article.

I don't discuss ViewState and ControlState here as the discussion is about ASP.NET MVC and not Webforms.

The Problem

Although hidden inputs sound great, there's one major flaw. The user can just go to the browser's "view source" option and actually see what value is persisted. This may not be a problem if you're simply storing a user's colour choice for example, but for sensitive data (for example, some id) that needs to be hidden from prying eyes (of the user and / or eavesdroppers), it can be a major issue.

The Solution

We try to solve the security issue by creating an Html Helper which will encrypt the data to be stored in the hidden input control. When the data is posted back, we need to ensure that consuming the data in the hidden input control is no more difficult than using a plain hidden input. To achieve this, we create a custom controller factory.

The Project

Let's create a default ASP.NET MVC 2 Web Application in VS 2010. Name it "HiddenEncryptDemo".

I installed MVC 2 RC 2 before installing VS 2010 RC. If you have some other combination, slight changes may be necessary.

The first thing I'm going to do is to create a very simple Computer.cs class in the Model folder:


public class Computer
    {
        public string Setting { get; set; }
    }

The reason I'm adding this class is to show that our approach will work not just on simple action parameters, but also on more complex types without a need for a separate model binder.

Next, add this to the Index.aspx view (in the /Views/Home folder):


<% Html.BeginForm("Something", "Home", FormMethod.Post); %>
        <%= Html.Hidden("computer.Setting", "hello world!") %>
        <input type="submit" value='submit' />
<% Html.EndForm(); %>

After that, add the following method to the Home controller:


        [HttpPost]
        public ActionResult Something(Computer computer)
        {
            ViewData["Message"] = computer.Setting;
            return View("Index");
        }

Nothing fancy, we're just setting the ViewData["Message"] to the computer.Setting value. Notice that in the code we added to Index.aspx, the form submitted to "Something" (the name of our action) and the name of the hidden input was "computer.Setting". The default model binder will find a value for "computer.Something" in the request parameters and upon seeing that it can set the Setting property of the computer parameter, it's going to set computer.Setting to the value it found (in our case, "hello world!"). If you run the project, you should see the index page with a submit button. Clicking the button will result in a post to the server where the Something action will get called. The Something action will then set the ViewData["Message"] and return the Index view. As such, you will see the words "hello world!" displayed on the page:

hello

On the initial Index page (or the page got after clicking the button), if you right click anywhere and select view source, you should see this (among other things):

hidden

Notice how the helper added a hidden input control, set its id to "computer_Setting" and name to "computer.Setting". The name is used as the key of the data in the post variables. Also notice how the value is "hello world!". The value is clearly visible to anyone looking at the source. Our goal is to come up with a way to hide that value from clear sight while at the same time ensuring everything works as smoothly and easily as it just did. A bonus would be if no change was required in the controller's code (i.e. no specific attributes needed on the consuming action or controller, no special model binding needed for the parameter etc.) – it would be unobtrusive.

The Encryption Provider

The first thing we'll need is an encrypting mechanism that's easy to use. Create a new folder in the project called "Helpers". [I know, I know, I should structure it better – but this is a demo :) ] Add a new interface called IEncryptString to the folder. The interface is very simple:


public interface IEncryptString
{
    string Encrypt(string value);
    string Decrypt(string value);
    string Prefix { get; }
}

Add an implementation of that interface called ConfigurationBasedEncryptionProvider:


public class ConfigurationBasedStringEncrypter : IEncryptString
{
    private static readonly ICryptoTransform _encrypter;
    private static readonly ICryptoTransform _decrypter;
    private static string _prefix;
    static ConfigurationBasedStringEncrypter()
    {
        //read settings from configuration
        var key = ConfigurationManager.AppSettings["EncryptionKey"];
        var useHashingString = ConfigurationManager.AppSettings["UseHashingForEncryption"];
        bool useHashing = true;
        if (string.Compare(useHashingString, "false", true) == 0)
        {
            useHashing = false;
        }

        var prefix = ConfigurationManager.AppSettings["EncryptionPrefix"];
        if (string.IsNullOrWhiteSpace(prefix))
        {
            _prefix = "encryptedHidden_";
        }
        byte[] keyArray = null;

        if (useHashing)
        {
            var hashmd5 = new MD5CryptoServiceProvider();
            keyArray = hashmd5.ComputeHash(UTF8Encoding.UTF8.GetBytes(key));
            hashmd5.Clear();
        }
        else
        {
            keyArray = UTF8Encoding.UTF8.GetBytes(key);
        }

        TripleDESCryptoServiceProvider tdes = new TripleDESCryptoServiceProvider();
        tdes.Key = keyArray;
        tdes.Mode = CipherMode.ECB;
        tdes.Padding = PaddingMode.PKCS7;

        _encrypter = tdes.CreateEncryptor();
        _decrypter = tdes.CreateDecryptor();

        tdes.Clear();
    }

    #region IEncryptionSettingsProvider Members

    public string Encrypt(string value)
    {
        var bytes = UTF8Encoding.UTF8.GetBytes(value);
        var encryptedBytes = _encrypter.TransformFinalBlock(bytes, 0, bytes.Length);
        var encrypted = Convert.ToBase64String(encryptedBytes);
        return encrypted;
    }

    public string Decrypt(string value)
    {
        var bytes = Convert.FromBase64String(value);
        var decryptedBytes = _decrypter.TransformFinalBlock(bytes, 0, bytes.Length);
        var decrypted = UTF8Encoding.UTF8.GetString(decryptedBytes);
        return decrypted;
    }

    public string Prefix
    {
        get { return _prefix; }
    }

    #endregion

}

You can see that in the class' static constructor, I'm reading in some configuration settings. While this goes against "convention over configuration", extensibility isn't really needed in terms of encryption keys. I though about using the machine key as the encryption key, but considering sever farm environments and other issues that could arise, I just chose to go with an app setting instead. Anyway, you can see that I'm using the configuration data to set up a couple of ICryptoTransforms that will be used for encrypting and decrypting. The class has two instance methods, Encrypt and Decrypt, which do pretty much what you'd expect. I'll explain the prefix part later.

The Html Helper

With the encryption provider out of the way, let's add our Html Helper. Create a new file called InputExtensions.cs in our Helpers folder and add the following code:


namespace HiddenEncryptDemo.Helpers
{
    using System;
    using System.Web.Mvc;
    using System.Web.Mvc.Html;

    public static class InputExtensions
    {
        public static HeartysoftHtmlHelper Heartysoft(this HtmlHelper helper)
        {
            return new HeartysoftHtmlHelper(helper);
        }
    }

    public partial class HeartysoftHtmlHelper
    {
        private readonly HtmlHelper _helper;
        private static readonly IEncryptString _encrypter = new ConfigurationBasedStringEncrypter();

        public HeartysoftHtmlHelper(HtmlHelper helper)
        {
            _helper = helper;
        }
        public MvcHtmlString EncryptedHidden(string name, object value)
        {
            if (value == null)
            {
                value = string.Empty;
            }

            string strValue = value.ToString();
            var encryptedValue = _encrypter.Encrypt(strValue);
            var encodedValue = _helper.Encode(encryptedValue);
            var newName = string.Concat(_encrypter.Prefix, name);

            return _helper.Hidden(newName, encodedValue);
        }
    }
}

Here, I'm firstly creating a "Heartysoft" helper which in turn has an EncryptedHidden method. This approach ensures that my helpers don't conflict with other helpers and in the view, I can do this: Html.Heartysoft().HiddenEncrypt(…). The EncryptedHidden helper is pretty simple. It takes the name and value to be used and encrypts the value. It also adds the prefix to the name. Remember our EncryptionProvider had a Prefix property? Here's where it comes in. So, if you pass in "computer.Name", the hidden input would have a name of "encryptedHidden_computer.Name" and id of "encryptedHidden_computer_Name". Of course, if you specify a different prefix in configuration, that would have been used instead. The prefix is used to determine which inputs need decrypted down the line. At the end of the EncryptedHidden method, we're using the default Html helper's Hidden method to generate the <input> tag.

Configuration

Go to web.config and ensure that under <configuration>, the following entries are present:


<appSettings>
        <add key="EncryptionKey" value="asdjahsdkhaksj dkashdkhak sdhkahsdkha kjsdhkasd"/>
</appSettings>

The View

At this point, go to Index.aspx and change the html of the form to:


<% Html.BeginForm("Something", "Home", FormMethod.Post); %>
       <%= Html.Heartysoft().EncryptedHidden("computer.Setting", "hello world!") %>
       <%--<%= Html.Hidden("computer.Setting", "hello world!") %>--%>
       <input type="submit" value='submit' />
   <% Html.EndForm(); %>

I've just commented out the old helper and used our new helper. If you run the page and click the button, you should see this:

encrypted-res

Notice that the message has disappeared. Viewing the html source, you should see that the html tag now looks like this:


<input id="encryptedHidden_computer_Setting" name="encryptedHidden_computer.Setting" type="hidden" value="TA9p71fDeNMfjlCPGOtK9Q==" />

Notice that although the "hello world!" value has been encrypted, the name has changed to "encryptedHidden_computer.Setting". When the form is posted back after the button click, the input parameter is thus "encryptedHidden_computer.Setting". Since there's no longer a "computer.Setting" input parameter, the model binder doesn't assign the computer parameter's Setting property with any value. The ViewData["Message"] thus stays empty and we se a blank on the result page.

So, our final challenge is to ensure that the model binder finds the original value. This will be done through a custom controller factory.

The Controller Factory

Our controller factory will look for any encrypted input parameters, decrypt them and add them to RouteData using their original names so that they're available to the model binder. Add a class called DecryptingControllerFactory:


public class DecryptingControllerFactory : DefaultControllerFactory
{
    private static IEncryptString _decrypter = new ConfigurationBasedStringEncrypter();

    public override IController CreateController(System.Web.Routing.RequestContext requestContext, string controllerName)
    {
        var parameters = requestContext.HttpContext.Request.Params;
        var encryptedParamKeys = parameters.AllKeys.Where(x => x.StartsWith(_decrypter.Prefix)).ToList();
        foreach (var key in encryptedParamKeys)
        {
            var oldKey = key.Replace(_decrypter.Prefix, string.Empty);
            var oldValue = _decrypter.Decrypt(parameters[key]);
            requestContext.RouteData.Values[oldKey] =  oldValue;
        }

        return base.CreateController(requestContext, controllerName);
    }
}

As you can see, we're identifying encrypted input parameters by checking which keys start with our encryption prefix. For each match, we get the value by decrypting and the original name by removing the prefix. We're not really changing the generated controller in any way – the controller factory just acts as a point in the request-response cycle where we can add parameters that are to be consumed by the model binder.

The last change you need to do is open up the global.asax.cs file and ensure the Application_Start looks like this:


protected void Application_Start()
{
    AreaRegistration.RegisterAllAreas();
    RegisterRoutes(RouteTable.Routes);
    ControllerBuilder.Current.SetControllerFactory(typeof(DecryptingControllerFactory));
}

We're just registering our controller factory on that last line.

Running the Page

We'll need to stop the dev server (if you're using IIS, simply restart it). We need to do this because we made changes to the global.asax.cs page. Right click the dev server's icon in your traybar and click stop:

stop-server

You can now simply hit ctrl + F5 in VS to run the page. Click the button and you should see this:

result

Finally, view the html source in the browser, and you should see that the hidden input's value is encrypted:


<input id="encryptedHidden_computer_Setting" name="encryptedHidden_computer.Setting" type="hidden" value="TA9p71fDeNMfjlCPGOtK9Q==" />

That means that our helper has encrypted the value for us and on postback, the controller factory is decrypting the value and adding it to the input parameters so that the model binder can correctly do its thing.

Possible Questions

1. Phil Haack already has a blog post stating how to add action method parameters here:
http://haacked.com/archive/2010/02/21/manipulating-action-method-parameters.aspx
Why didn't you use that method to add the input parameter?
The method described in that post uses an action filter. There are two downsides to that. First, you'd need to go about and decorating your controllers and actions with some [Decrypt] attribute. That's obtrusive. Second-and-more-important, action filters run after model binding. That would work for simple parameters for action methods, but for complex parameters (i.e. viewmodels) or parameters that need complex model binding, that simply won't work. My approach here adds the input parameter before the model binding stage and thus, the model binders can access the correct values without changing anything.

2. Why didn't you use a ModelBinder attribute for decryption?
That could have worked, but would have required you to use the model binder whenever you intended to use the Html.Heartysoft().EncryptedHidden() helper. That's obtrusive and forgetting to set an attribute could have caused nightmares. Furthermore, your other model binders would not have recognised the values. The approach outlined here will work with any and all existing model binders.

3. Is the data really secure?
The value sent to the browser is encrypted using a key. The key is never sent to the browser. As long as the key is secure, the data is as secure as the encryption is.

4. Your code structure is crappy.
It's a demo…deal with it ;) The code is pretty straight forward and you can use your our design patterns and abstractions to make it more your-framework-friendly.

5. I changed the key but the change isn't "happening". What gives?
The encryption key and whether to use hashing or not settings are read during the static constructor of our ConfigurationBasedEncryptionProvider. If you need to change the values, restart the server. I figured you didn't need to change these at runtime and thus put them in the static constructor so that they are read in only once.

6. Why a ControllerFactory? Why not in Begin_Request in global.asax.cs?
It seems that adding the parameters in Begin_Request doesn't seem to persist further down the line. Meaning, I tried that and the new parameters added seem to disappear. Hence the controller factory. Adding the parameters at that stage seems to allow the model binder access to the parameters without any problems. Perhaps a custom ActionInvoker would also have worked, but would have needed setting on each controller – or in a custom controller factory. If the latter, then why bother with the ActionInvoker when the factory can add the parameters itself?

7. Why do you add the parameters to RouteData and not Request[…] or Request.Params[…] or Request.Form[…] etc.?
Coz those are readonly.

Source Code

Download

kick it on DotNetKicks.com  Shout it

---------------------------------------------------------

Questions  and comments relating to this article are welcome. Comments completely unrelated to the article and posted with the sole intention of putting your link here are not.

If you spam, your comment will not be approved, will be deleted and your IP blocked. I maintain my site almost daily and such comments – even if they pass the spam filter – will get removed as soon as possible. If this gets too tedious, I may disable comments entirely. Please don't ruin it for everybody else.

---------------------------------------------------------

Share or Bookmark this post…
  • Facebook
  • DotNetKicks
  • Digg
  • LinkedIn
  • Technorati
  • del.icio.us
  • Google
  • Live
  • Tumblr
  • msdn Social
  • Ping.fm
  • Reddit
  • Slashdot
  • StumbleUpon
  • TwitThis

Asp.net MVC 2.0 RC 2 Just Released

by ashic 4. February 2010 19:47

I was just moderating the asp.net forums and approved Phil's announcement 29 minutes ago that Asp.net MVC 2.0 RC 2 has just been released:

Hi everyone,

We released ASP.NET MVC 2 Release Candidate 2 this evening!

This release works with Visual Studio 2008 SP1 and Visual Web Developer 2008 SP1. We will be posting source code and our futures assembly soon.

Thanks,

The ASP.NET Team

Phil Haack (http://haacked.com/)
Senior Program Manager, Microsoft

Pretty cool…can’t wait for the RTW.

Share or Bookmark this post…
  • Facebook
  • DotNetKicks
  • Digg
  • LinkedIn
  • Technorati
  • del.icio.us
  • Google
  • Live
  • Tumblr
  • msdn Social
  • Ping.fm
  • Reddit
  • Slashdot
  • StumbleUpon
  • TwitThis
Categories: ASP.NET | ASP.NET MVC

Ajax, Asp.net Ajax, jQuery – Misconceptions and Facts

by ashic 9. December 2009 08:19

There seems to be some confusion among a lot of developers about Asp.net Ajax. This article will hopefully clear some doubts regarding what it is, what it isn’t and where jQuery comes in.

Ajax

Ajax stands for Asynchronous JavaScript And Xml. Ajax is used to asynchronously make a server side request from a web page. It does not need a full page refresh (a full postback in Asp.net terms) and can result in a few benefits:

1. The user does not have to see the whole page flash blank for every little thing that requires server side processing.

2. Only the required data is sent from the server to the client in response to an Ajax request. This reduces network traffic.

3. Only the required data is processed. So processing of other parts of the page is not needed. This reduces the server workload.

Microsoft created the XMLHTTP ActiveX control way back in 1999. Other browsers have added similar functionality in a native object called XmlHttpRequest (XHR). These objects are used to create and carry out Ajax requests. The technology was being used before the term Ajax officially came into being, and it became widely known when Google used it with GMail and Google Maps. Jesse James Garrett coined the name Ajax feeling the need for an appropriate shorthand. Presumably, he thought of the name while taking a shower in 2005.

Although the name suggests JavaScript and XML – and initially XML was used to transfer the data – nowadays data can be passed in other formats as well as XML. For instance, it’s very common to pass data between the server and client using JSON (JavaScript Object Notation). And although the XHR object is usually used to make Ajax calls, other methods like using an IFrame or dynamically created <script> tags can also be seen.

What Ajax Can’t Do

There are a few limitations to Ajax calls. The major ones are:

1. It can’t make a cross domain call. The request must be made to a url within the same domain. So, if your domain is www.yoursite.com, the request must go to a url within www.yoursite.com. It can’t go to www.darth-vader.com or anywhere else. [There are workarounds, like using JSONP – although whether JSONP is Ajax is subject to speculation.]

2. It can never ever upload a file to the server.

3. It can’t access files on the client machine.

4. Any other limitations that JavaScript has.

Asp.net Ajax

Asp.net Ajax is Microsoft’s framework for Ajax and JavaScript development. Although it’s name is Asp.net Ajax, it can do much much more than just Ajax. It has always consisted of client side JavaScript and integration into Asp.net WebForms. This has been a main point of confusion and caused many people to think that Asp.net Ajax can only be used from Asp.net. Along with that, there’s also the Asp.net Ajax Control Toolkit (ACT), which adds a bunch of functionality and features on top of Asp.net Ajax. If this all seems confusing, it’s probably because it is. And Microsoft has addressed this. The Asp.net Ajax framework has recently been reorganized and the new version is currently in Beta. To make things simpler, these are now the key aspects of Asp.net Ajax:

  • Asp.net Ajax Library: This is a reorganization or the existing client side features (along with quite a few new ones) of Asp.net Ajax. This is a stand alone JavaScript library just like jQuery, scriptaculous, mootools etc. Although called Asp.net Ajax, it is in fact an entire JavaScript library that supports Object Oriented JavaScript, client templates, web service calls, animation, JSONP and a whole host of other things. It can be used with server side Asp.net, Asp.net MVC – even plain Html. Want to use it on a php page? Go right on ahead. It will work perfectly without .Net even installed on the server. This is completely client side, with no server dependencies. To add to that, the Asp.net Ajax Control Toolkit (ACT) has been incorporated into this library. So, you can use stuff like the CalenderExtender, ModalPopupExtender, HoverMenuExtender etc. without needing to use Asp.net. The Asp.net Ajax Library project is open source and maintained by the Codeplex Foundation (Microsoft recently donated the project to the Codeplex Foundation). You can find more information about the library (including detailed tutorials) at http://www.asp.net/ajaxlibrary/.

 

  • Server Side Asp.net: Asp.net Ajax has rich server side integration into Asp.net. To utilize these features, you will need to be using Asp.net. The ScriptManager, UpdatePanel, UpdateProgress and other controls allow developers to use features like partial page updates, managing browser history, script combining, web service script registration etc. without having to write any JavaScript. Asp.net Ajax enables Ajax features while retaining drag and drop RAD functionality in WebForms. It also provides an infrastructure on which to build extender controls, script controls and components – custom server controls that use the Asp.net Ajax Library (the client side library mentioned above) – and takes care of registering the required scripts and resources at runtime. All the developer using the control needs to do is drag and drop the control onto a WebForms page with a ScriptManager and set the properties. The framework will do the rest.

 

  • Ajax Control Toolkit: The Ajax Control Toolkit (ACT) adds a bunch of features and controls to Asp.net Ajax. Previously, this was a stand alone project hosted at codeplex. It was primarily used in WebForms scenarios. With the new-and-currently-in-beta version, the ACT has been incorporated into the Asp.net Ajax Library. All the controls of the toolkit are all present and can be used entirely from script. The ACT also has some server side components which can be used to make custom Asp.net Ajax server side control development easier. In addition, the server side components provide drag and drop functionality to all the controls and extenders in the client side ACT library. This makes using the controls easier in a WebForms environment, taking advantage of the ScriptManager control. While very easy to use with WebForms, it’s also very simple to use entirely from script (like in Asp.net MVC or plain Html). This has been a major improvement in the latest beta as it was quite cumbersome to use the toolkit before.

 

  • jQuery: jQuery is another JavaScript library, just like the Asp.net Ajax library. It’s free, open source and the brainchild of John Resig, who initially released it in 2006. So why are we talking about it here? Well, jQuery has quite a few benefits and is a great JavaScript library. It’s very lightweight, has some awesome selector syntax as well as chaining support for expressions. Again, why am I mentioning this here? The Asp.net Ajax team was looking to incorporate similar features in the client side library of Asp.net Ajax. jQuery was gaining real popularity at that time. It could do complex JavaScript tasks with very little code. The motto of jQuery is “write less, do more”. Microsoft was also pushing the early versions of Asp.net MVC. Asp.net MVC really needed a JavaScript library that could handle Ajax, JSON and client side manipulation in a simple manner. More and more developers started using jQuery for this. It was decided that jQuery would be officially considered a part of Asp.net development. It was initially included into the Asp.net MVC project template, and since then has been included in Asp.net Ajax as well. All the controls in the ACT now work seamlessly with jQuery (in the latest beta of the Ajax Library). Microsoft also pledged bug reports, patches, tests etc. to the jQuery project. One thing to note is that although jQuery support is included in the Asp.net Ajax Library, you do not need to be using jQuery to use Asp.net Ajax (or even the client side Asp.net Ajax Library). Microsoft recommends using them both together as it will make development easier with less need to reinvent the wheel. jQuery is already a rocket powered missile in a tiny package. As such, jQuery complements Asp.net Ajax and using one does not force you to restrain from the other.

 

  • Microsoft Ajax Content Delivery Network (CDN): Microsoft has CDN servers around the globe. And it hosts all of the JavaScript files in the Microsoft Ajax Library (including those for the ACT), jQuery, jQuery validation plug-in and the Asp.net Ajax scripts for Asp.net MVC on these servers. The files are cached and delivered to users of your website from their nearest location. This has a a few benefits:
    • The files reach the website user faster.
    • The files, once delivered, are cached on the browser too. Any other domain that uses the same file causes the browser to use the local cached version (since the file’s url is the CDN url, the domain remains constant). Hence, other websites using the CDN will load faster too.
    • The files are delivered from Microsoft’s servers. This reduces your server network bandwidth usage and reduces costs for you.
    • The CDN supports Http and Https. This means website users won’t get a pesky message asking them whether or not they want to show non-secure items.

You can learn more about the CDN here: http://www.asp.net/ajaxlibrary/CDN.ashx

So that’s it for an overview of what Asp.net Ajax actually is. If you wish to learn more, you can find a whole host of resources here: http://www.asp.net/ajax/

kick it on DotNetKicks.com
Shout it
Share or Bookmark this post…
  • Facebook
  • DotNetKicks
  • Digg
  • LinkedIn
  • Technorati
  • del.icio.us
  • Google
  • Live
  • Tumblr
  • msdn Social
  • Ping.fm
  • Reddit
  • Slashdot
  • StumbleUpon
  • TwitThis

Asp.net MVC, Html.DropDownList and Selected Value

by ashic 26. March 2009 23:53

I recently ran into the altogether common problem of the Html.DropDownList helper rendering a drop down list with no value selected. This is a major problem when editing data as by default, the first value is selected and saving would mean the first value is used.

There have been a few issues resulting in the same error. My issue was that I was setting the Name of the drop down list to be equal to the property on my model. I was using the Entity Framework, and had an Image class with a navigation property called Category. I was using this to render the ddl:

<%= Html.DropDownList("Category", (IEnumerable<SelectListItem>)ViewData["categories"])%>

In my controller, I was setting the ViewData like this:

this.ViewData["categories"] = new SelectList(db.CategorySet.ToList(), "CategoryId", "Title", img.CategoryReference.EntityKey);

Unfortunately, even though I had set the selected value (third parameter to the SelectList constructor), the ddl had no value selected.

The fix was quite simple:

<%= Html.DropDownList("CategoryId", (IEnumerable<SelectListItem>)ViewData["categories"])%>

I just changed the Name of the drop down and handled the assignment in the controller.

The reason behind this problem is that asp.net MVC first looks for a match between the name of the drop down and a property on the model. If there’s a match, the selected value of the SelectList is overridden. Changing the name of the drop down is all it takes to remedy the issue.

 

Hope that helps.

Share or Bookmark this post…
  • Facebook
  • DotNetKicks
  • Digg
  • LinkedIn
  • Technorati
  • del.icio.us
  • Google
  • Live
  • Tumblr
  • msdn Social
  • Ping.fm
  • Reddit
  • Slashdot
  • StumbleUpon
  • TwitThis
Categories: ASP.NET | ASP.NET MVC

Powered by BlogEngine.NET 1.6.1.0
Theme by Ashic Mahtab

Need an expert?

Ashic Mahtab
ashic@live.com
(+44) 07879927393

Stats

Featured Ads

 

Donations

I maintain this site and create all content entirely in my own time just to help you guys out. If you find the stuff helpful, or cool or just like what you see, I'd appreciate you chipping in to help out with the hosting costs. It's easy to do so - just click the button below - no amount is too low :)