ASP.Net MVC Framework - Security

Note: This post is based on an early version of the ASP.Net MVC Framework, not the final version, so changes my happen in the future.

When using the ASP.Net MVC Framework it will do a URL routing to route an URL to a Controller, and the Controller should execute some logic and render the Views. We can for example use an URL like /Products/Edit/1 to edit a product. By using this kind of URL we need to care about security, because we don’t want everyone to be able to edit a product. How can we prevent some users to not edit the product? We can’t use the <location> element and deny certain user for accessing the View.

<location path="Views/Products/Edit.aspx">
   <system.web>
     <authorization>
       <deny users="*"/>
     </authorization>
   </system.web>
</location>

The above section will not work, well sort of; it will make sure no one can access the Edit.aspx page. But if we use the RenderView method to render a View the <location> element will not work, the View will still be rendered. So what other options do we have? When we use the MVC framework the Controllers are responsible to handle the logic and the render of Views. We can in this case add the security check into our Controllers, in this case into the Action methods. As you may know a Controller can have several actions, like Edit, Update and List etc and they can also render different Views. Maybe the List action should everyone be possible to call, but not the Edit or Update methods. So in this case we need to add the security check on the Edit and Update methods in a Controller. This can for example be done by using the Roles feature of ASP.Net:

[ControllerAction]
public void Edit(int? id)
{
  if (!Roles.IsUserInRole("Admin"))
     throw new SecurityException("Access denied");
  ... 
}

To avoid writing this check in the Action methods we can instead use the PrincipalPermissionAttributes shipped with .Net:

[ControllerAction]
[PrincipalPermission(SecurityAction.Demand, Role="Admin"]
public void Edit(int? id)
{
   ...
}

If we want to make sure all Action methods in a Controller have the check, we can add the PrincipalPermissionAttribute to the Controller class:

[PrincipalPermission(SecurityAction.Demand, Role="Admin")]
public class HomeController : Controller

If we want to handle the SecurityException we can use the ExceptionHandlerAttribute I wrote about in my previous post. This can catch the SecurityException and Render a View that will display the exception message.

[ControllerAction]
[PrincipalPermission(SecurityAction.Demand, Role="Admin"]
[ExceptionHandler("Error", typeof(SecurityException))]
public void Edit(int? id)
{
   ...
}

If we don’t want to use the PrincipalPermissionAttribute and instead write our own Security handler, we can override the OnPreAction method and implement the security check. The OnPreAction method will be executed before any Action methods are executed.

protected override void OnPreAction(string actionName, System.Reflection.MethodInfo methodInfo)
{
   if (actionName == "Edit")
   {
     if (!Roles.IsUserInRole("Admin") || !User.Identity.IsAuthenticated || !User.Identity.Name = "Administrator")
        throw SecurityException("Access denied!");
   }
}

4 Comments

  • In the OnPreAction method, I suppose that the methodInfo parameter gives us access to the id parameter of the action in case we want to check if the user is authorized to edit that particular item, right ?

  • I've always seen hard-coding this information into the page as a "bad thing". This determination really belongs in your security module's AuthorizeRequest event. In an ideal world, asp.net's MVC system would make it's determination as to Controllers and Actions before AuthorizeRequest so authorization could be done based on the controller & action.

  • Fredrick,
    I found that I can't use [PrincipalPermission] at the class level as a simple way to protect all action methods, as it seems to apply even to the default constructor.

    That is, the controller class hits the security exception at instance creation time, before any other error handling methods in a controller have a chance to operate.

    However, placing the PrincipalPermission attribute on individual methods seems to work well. Is there a way to exclude certain methods (like the constructor) from a PrincipalPermission applied at the class level?

  • Great article!

Comments have been disabled for this content.