Friday, September 1, 2017

Building a custom security framework with ASP.NET applications

How could we extend the default ASP.NET framework with our custom implementation?

And since we've been talking about security, one interesting request that came to me was to redo part of the security framework one of our applications (front/back ends). Of course, we already had permissioning in place but the recent requirements required more granular configuration on the application while keeping part of the previous features.

The current implementation

The current implementation is based on Role Providers which's simple enough to implement assuming that you can find a good cake recipe =) . We chose it because the access level could be determined by user versus controller.
So, if you have some customization, you are required to do two things: implement your custom role provider and use the AuthorizeAttribute and specifying the Role name as:
[Authorize(Roles = "MyRole")]
Then you're good to go. The framework will do most of the boring work for use and assuming you implemented your role provider correctly by implementing System.Web.Security.RoleProvider, the framework would resolve the authorization for you.

The new requirement

The problem is that the new requirements require a more granular approach that cannot be accomplished with Roles. And since I'm simplistic by nature, using the out of the box solution wouldn't work because by being too generic (which most of the time won't solve 100% of developers' use cases), it would require me to write stuff I never needed over complicating my solution which opens ports to bugs, especially in this critical area of the application.

Normal, that's what you get when you use an out of the box solution. So, most of the developers, would probably following my path and write some custom implementation on top of that.

So back to the requirement, the new permissioning model is basic. Based on User, Groups, Roles, Permissions (or Claims) and the traditional relationships between them. Users can view everything but only be able to act on certain contexts.

In terms of development, the following use cases are derived from it:
  • the web app should render / hide controls (buttons, links) to be even rendered (note that we don't want to do this with Javascript!);
  • the web app should validate on each endpoint if the user is authorized and has permissions to call that endpoint;
  • we should be able to somehow map: users -> groups; groups -> roles; roles -> permissions;
  • we user logs in, we need to load his permissions;
  • every message sent to the backend (assuming you have a decoupled backend) should have a SubmittedBy and that should be validated against the database, when processing, if that user had access to that feature in the endpoint.
  • Reject the request if he doesn't have the permission (either in the front or in the backend);
  • Log that as a SecurityException or what is relevant for you.

Introducing Claims

Claims are nothing more than granular permissions linked to resources in your application rather than role based permissions. That way you can drill right down and control access to each resource/action individually rather than having a multitude of roles for each one.

I personally like to use a mixture of both so I can use the standard authorization tags in MVC with roles, which read the claims and use granular claims/permissions to make the claims authorization manager pickup the more complicated rules.

The Solution

So the solution is broken in 4 parts:
  • The basic Infrastructure - all the code necessary to provide access to the permissioning system, validation, constants, etc.
  • Asp.Net - secure the web app so that all actions are authorized;
  • Backend - validating if the requests reaching the backend also respect the same concerns already printed in the previous layers;
  • Possibly, you will be required to refactor your framework to apply authorization in certain parts of your application. Or even, creating a generic application filter to auto-process every command sent to it. Topic for a future blog post.
Before writing code though let's not forget some to review some good design practices:
  • Code Reuse - centralize your roles in one single class in an assembly so everyone talks the same dialect
  • Use consts / readonly strings - please, don't propagate hardcoding your permissions everywhere on your code
  • Write some extension methods / helper classes so you respect the single responsability principle
  • Performance - I used Dependency Injection to inject from the DB the permissioning configuration into my PermissionController class in case it would be loaded only once and kept in cache for the duration of the application. In case you forgot it, just set some code in your Application_Start  as it's your MVC's composition root
  • Review security principles
  • Write tests

The Basic Infrastructure

I like writing code bottom up so I can unit test my code as I'm writing it. So, the 1st thing I created on this project was a SecurityController or, a facade to control all my custom authorization:


The Init static method is be used to initialize my framework. It gets roles and groups from the db, memory or whatever you're using. That way, the logic is db-independent. Meaning it can be reused everywhere, is lightweight, can be easily cached and, performant by default.

The other methods are pretty self-descriptive, right? Interesting is that, because this class is so light weight, it can be reused trough all your assemblies, tests and even different projects. And also can be easily extensible but still closed for modifications.

Initializing the Framework

In order to understand composition roots if we want to do proper DI. Composition roots are basically entry points that are run once and should be used to initialize stuff for your application to run properly.

In our case, if we divide our application in two layers, we would have:
  • Asp.Net - global.asax;
  • A Console Application - your static void main;
  • Backend - varies according to the technology.

For my simple Asp.Net app, the composition root is the Application_Start method. So, we simply have to inject your initialization in our Asp.Net MVC app:
 

Securing the Web App

For our web app we want to secure our:
  • Views - render/hide stuff in razor according to user's permissions
  • Controllers - authorizing/revoking access on controllers and actions
  • ActionFilters - to auto-validate content that's being injected.

Securing our Views

The first obvious step is to hide content user isn't supposed to see. Remember, we should not do this via javascript as client-side security doesn't work. In order to hide content, we need to tell Razor not to render the content if the user doesn't have access to some resource. That could be done simply like this:


Here, I have a few interesting points:
  • using an extention method in my Razor to reduce duplication of code;
  • Moved all my permissions to the "Permission" class - to avoid hardcoding permission names troughout the application;

While it's true that the above code would work, more interesting would be trying to implement it elegantly by extending the HtmlHelper to create custom methods such as SecureLink and SecureButton and using those controls in Razor:

The advantage of this approach is to simplify your Razor markup and reuse the logic to validate like this:
@Html.SecureButton("Edit", "btn", Permissions.Edit)

Securing Controllers

In order to secure controllers, we'll need 2 things:
  • create an attribute to auto-validate requests to a given action/controller;
  • secure your actions/controllers trough application filters;
  • Option: run additional validation if you still think makes sense;

The RequirePermission ActionFilter

I created the RequiredPermission Attribute to validate, as an ActionFilter all the requests issued to my app. It looks more a less like:

Decorating the Views

With all of that out of the way we can now decorate our actions like this:
Take a look at the RequirePermission attribute there. With one liner in the controller, we got the validation working in Asp.Net, using our custom security implementation. All the logic is now centralized on the RequirePermission ActionFilter what is what we wanted.

Now, it's just a matter of manual work to decorate your endpoints with the validations you want. Remember that we did all this work because we now require granular permission validation. Else, we would still be using the Role-based validation.

The Backend

Because we separated the concerns in our application, delegating to the SecurityController all the logic to validate permissions against user, our backend, whatever it is, can re-utilize it.

Conclusion

On this post we analysed a custom implementation for securing Asp.Net applications here. There's a lot more to talk about this topic that will be reviewed in future blog posts.

See Also

Why you should start using .Net Core
Package Management in .Net Core
Exporting Html to Pdf using only JavaScript
Importing CSVs with .Net Core and C#
Exporting a CSV generated in-memory in Asp.Net with C#
Building and Running Asp.Net Core apps on Linux
Asp.Net / Asp.Net Core: Generating views in the backend
Testing Javascript on Asp.Net Core Applications