Blog

Follow my blog for some tech how-tos and advice for entrepreneurs.

Switching Views without changing the Controller in ASP MVC 3 Razor

26 Jan 2012

Imagine, your boss comes in with this requirement:

This web application you just finished, can you do another one that does (mostly) the same but has a completely different look and slightly modified flow?

This happened to me. Needless to say, the last thing, you want to do is copy and paste the whole project. This would mean twice the work for any future changes.

Luckily, there is a way to minimise code duplication in such a situation. Without any change to the controller, you can have your application deliver different views based on a configuration key in web.config. In the rest of the article I will refer to the different look and feel as “brand”.

Here is how to achieve this:

0. Planning

One way to organise the views in a folder structure.

The views under Brands/MyBrandA.com contain the views specific to BrandA.com, the views under Brands/MyBrandB.com are specific to BrandB.com and the View folder in the root contains the Views shared by both brands.

Importantly, every Views folder needs a Web.Config, otherwise the Razor engine throws errors. The easiest way is to copy the Web.configs from the original views folder.

1. Create a custom View Engine

The next step is to tell MVC, where to look for your Views. The way to do this is to create your own ViewEngine by overriding RazorViewEngine:

public class BrandViewEngine : RazorViewEngine
{
  public BrandViewEngine(string s)
  {
    var specificViews = new string[]
    {
      "~/Brands/" + s + "/Views/{1}/{0}.cshtml",
      "~/Brands/" + s + "/Views/Shared/{1}/{0}.cshtml",
    };

    var specificAreaViews = new string[]
    {
      "~/Brands/" + s + "/Areas/{2}/Views/{1}/{0}.cshtml",
      "~/Brands/" + s + "/Areas/{2}/Shared/Views/{1}/{0}.cshtml"
    };

    AreaMasterLocationFormats =
      specificAreaViews.Union(AreaMasterLocationFormats).ToArray();
    AreaPartialViewLocationFormats =
      specificAreaViews.Union(AreaPartialViewLocationFormats).ToArray();
    AreaViewLocationFormats =
      specificAreaViews.Union(AreaViewLocationFormats).ToArray();
    MasterLocationFormats =
      specificViews.Union(MasterLocationFormats).ToArray();
    ViewLocationFormats =
      specificViews.Union(ViewLocationFormats).ToArray();
    PartialViewLocationFormats =
      specificViews.Union(PartialViewLocationFormats).ToArray();
  }
}

The *Formats variables are folder lists where MVC looks for Views. You simply need to put our branded paths in front of the default paths, so the ViewEngine will look in our branded folders first.

2. Activating the ViewEngine

In the Global.asax.cs file, the Application_Start() method needs to register our new ViewEngine.

protected void Application_Start()
{
  ViewEngines.Engines.Clear();

  string myBrandString =
    ConfigurationManager.AppSettings["Brand"].ToString();

  ViewEngines.Engines.Add(new BrandViewEngine(myBrandString));

  AreaRegistration.RegisterAllAreas();
  RegisterGlobalFilters(GlobalFilters.Filters);
  RegisterRoutes(RouteTable.Routes);
}

Now to switch between brands all that is needed is a key in your Web.config:

<add key="Brand" value="MyBrandA.com"/>

3. Using the branded layout file

If a particular View is not found in the brand folders, the engine will fall back on the Views in the default folder. However, you would probably want to display the branded layout file even for non-branded Views. To do this, the _ViewStart.cshtml (the one in the default folder, not in the branded folders) needs to work out, which layout file to use. This can be done like so:

@{
  var brand = System.Configuration
    .ConfigurationManager.AppSettings["Brand"].ToString();
  Layout = "~/Brands/" + brand + "/Views/Shared/_Layout.cshtml";
}

That’s it. You now have completely separate sets of HTML markups for your sites. You can switch between them using a configuration setting and no change to the controller logic is required.

Please leave a comment if you find a mistake or have a suggestion.

Thanks, Thomas

Secure Salty Password Hashing into a fixed length String in C#

13 Nov 2011

This is an example of using one of the .NET hashing algorithms to hash a password into a fixed length string which can then be stored in a fixed length database column. I’ve started from this example implementation.

Password hashing basics

Generally speaking, a hash function always creates the same output for the same input. However, the output does not have to be unique. For example, “mod 5” can be used as a hash function h() for integers:

h(3) => 3
h(6) => 1

but also

h(8) => 3

More advanced hash algorithms are MD5 or SHA which convert an input string (like your password) into an output string that looks like random data, commonly referred to as the Hash.

h("MyPass") => "MyHash"

The same input string will always create the same output. Using one of the advanced algorithms, it is impossible to “decrypt” the Hash into the original password.

What is possible for an attacker is trying to guess the password based on dictionaries of common words or phrases.

To avoid these dictionary attacks, passwords can be salted.

A Salt is a randomly generated string which is added to the password:

h("TheSalt" + "MyPass") => "MySaltedHash"

In order to verify a password, the Salt needs to be stored as well, for example next to your hash, so your password column in the database would contain:

"TheSalt" + "MySaltedHash"

Problems with the reference implementation

Depending on the algorithm and the BASE64 encoding, the string length is not guaranteed. Therefore, the resulting hash must be concatenated to guarantee a certain string length. Since the salt is at the end of the hash, the concatenation will cut off the salt, which makes the verification of the hashed password impossible. In order to solve this, the salt has been fixed to a specific length and stored at the beginning of the hashed string.

The Implementation

Let’s look at the implementation. This should be the minimum namespace references required:

using System;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using System.Security.Cryptography;

Some constants:

// maxHashSize needs to be a multiple of 4 for the Base64 Encoding
protected static readonly int maxHashSize = 48;

// Length of the random salt. Can be any length.
protected static readonly int saltSize = 5;

maxHashSize is the maximum string length of the generated Hash (including the Salt). Note, that this length must be a multiple of 4 because of the BASE64 encoding. If, for instance, your password column in the database is 50 characters wide, a maxHashSize of 48 is a reasonable choice.

saltSize is the number of bytes to use for the Salt.

public static string ComputeHash(
  string myPassword,
  byte[] saltBytes = null)
{
  // randomly create the Salt
  // unless it has been passed in
  if (saltBytes == null)
  {
    saltBytes = new byte[saltSize];
    RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider();
    rng.GetNonZeroBytes(saltBytes);
  }

  // Concat the Salt and Password
  byte[] myPasswordBytes = Encoding.UTF8.GetBytes(myPassword);
  byte[] myPasswordWithSaltBytes = 
    saltBytes.Concat(myPasswordBytes).ToArray();

  // Hash the Salt + Password
  HashAlgorithm hash = new SHA512Managed();
  byte[] hashBytes = hash.ComputeHash(myPasswordWithSaltBytes);
  byte[] hashWithSaltBytes = saltBytes.Concat(hashBytes).ToArray();

  // Convert to BASE64 and cut off after the maximum size
  string hashValue = Convert.ToBase64String(hashWithSaltBytes);
  return hashValue.Substring(0, maxHashSize);
}

This method creates a random Salt unless a Salt was passed in. Then, the Salt is concatenated with the password and hashed. For the hashing function, any class that implements HashAlgorithm can be used, see here for a list. In general, the *Managed classes can be used in most cases. If you have special government certification requirements, there are *CNG classes which are FIPS certified.

public static bool VerifyHash(
  string myPassword,
  string hashValue)
{
  // Convert base64-encoded hash value into a byte array.
  byte[] hashWithSaltBytes = Convert.FromBase64String(hashValue);

  // Copy salt from the beginning of the hash to the new array.
  byte[] saltBytes = hashWithSaltBytes.Take(saltSize).ToArray();

  // Compute a new hash string.
  string expectedHashString = 
    ComputeHash(myPassword, saltBytes);

  // If the computed hash matches the specified hash,
  // the myPassword value must be correct.
  return (hashValue == expectedHashString);
}

Let’s see it in action:

public static void Main (string[] args)
{
  string myPass = "MySecretPassword";
  string myHash = ComputeHash(myPass);

  if (VerifyHash(myPass, myHash))
  {
    Console.WriteLine("The password was correct!");
  }
}

All that is left to do is saving myHash to the database to recall it when needed to verify a password.

That’s it!

Please leave a comment if you find a mistake or have a suggestion. Thanks again to the reference implementation, I used as a starting point.

Adding a Captcha to an MVC application

27 Sep 2011

It took me ages to find and integrate this, so maybe someone will find this useful.

The task is to add a captcha to the user registration screen of an MVC application. But I didn’t want to include any 3rd party libraries, rather use an existing web service.

After some research, I found OpenCaptcha.com which seems to provide just this. So here is a guide on how to plug this into your MVC application.

The starting point is a MVC 3 app with forms based authentication.

Amend your AccountController.Register method:

public ActionResult Register()
{
  ViewBag.CaptchaGuid = Guid.NewGuid().ToString("N");
  return View();
}

This provides a random string which is then used to request the captcha image from the site.

Next, amend your Register.cshtml with the following lines.

@Html.ValidationMessageFor(m => m.ConfirmPassword)
</div>

<div class="editor-label">
  Captcha
</div>
<div class="editor-field">
  <img src=@Url.Content("http://www.opencaptcha.com/img/"
    + ViewBag.CaptchaGuid + ".jpgx") alt="Captcha" />
</div>

<div class="editor-label">
  Please enter the string above.
</div>
<div class="editor-field">
  @Html.TextBox("Captcha")
</div>

<p>
  <input type="submit" value="Register" />
</p>

Now, the Captcha should be visible along with an entry box for the user to submit the answer. Since we also need the Guid, let’s transport it through a hidden field. Add this to Register.cshtml:

@Html.Hidden("CaptchaGuid", ViewData["CaptchaGuid"])

Lastly, we need to verify, that the user’s string matches the one in the picture. To do this, we simply construct a URL to OpenCaptcha which answers the question by returning “success” or “fail”. Amend your AccountController.Register (the POST version) like this:

//
// POST: /Account/Register
[HttpPost]
public ActionResult Register(RegisterModel model)
{
  string CaptchaGuid = Request.Form["CaptchaGuid"];
  string Captcha = Request.Form["Captcha"];

  WebClient wc = new WebClient();
  string CaptchaResponse = wc.DownloadString(
    "http://www.opencaptcha.com/validate.php?img="
      + CaptchaGuid + "&ans=" + Captcha);

  if (!"success".Equals(CaptchaResponse))
  {
    ModelState.AddModelError("",
      "Captchas didn't match, please try again!");
  }
  
  if (ModelState.IsValid)
  {
    // Attempt to register the user

That’s it!

Note: It is probably a good idea to wrap the OpenCaptcha requests into a try-catch in case, the service is down. I left that out for readability.

Thomas Glaser

@tkglaser
...

About me

Web & Mobile Engineer, Founder, Lean Startup Enthusiast.

tk glaser consulting

Social Links