In this post I will be structuring my ASP.NET Web Project following the principles outlined in the Web Application frame:
- Exception Management
- Logging, Instrumentation
- Page Layout (UI)
- Page Rendering
- Presentation Entity
- Request Processing
- Service Interface
- Session Management
Authentication & Authorization
IIdentity and IPrincipal interfaces are the heart of .Net authentication and authorization. All authentication and authorization implementations implement these interfaces. Concrete classes that implement the IPrincipal interface populated with concrete classes that implement IIdentity and stored in HttpContenxt.Current.User where authorization frameworks such RoleManager will look for it.
I have not decided where my application will be deployed – Internet, Intranet, or Azure. So the best bet for me in regards to Authentication and Authorization would be to assume this:
- The concrete Authentication & Authorization framework will be configured in Web.Config without affecting the code.
- Authorization logic will be made by Authorization framework or in code based on concrete implementation of IPrincipal located in HttpContext.Current.User anyway.
Main decision to make at this point is to structure the project’s folders the way that there is clean separation between restricted pages and resources that do not require authentication and authorization. Following is how my web application is structure in terms of folders:
The following folders I have created manually:
- Controls. This folder will hold user controls, ASCX’s. Does not require authentication. Direct access to ACSX files will be restricted by ASP.NET runtime.
- CSS. This folder will hold CSS stylesheet files. Does not require authentication. It will be configured for anonymous access purposefully.
- IMG. This folder will hold image files (JPG, PNG, etc.). Does not require authentication. It will be configured for anonymous access purposefully.
- Restricted. This folder will hold any use case that requires authentication/authorization.
Minimum Authorization configuration in Web.Config:
From p&p’s ASP.NET 2.0 Internet Security Reference Implementation
Partition the site into restricted areas and public areas
All pages that require authenticated access are placed in either the Restricted or Admin folders. Pages and resources in other directories are anonymously accessible. By partitioning the site, SSL is used only on those pages that need it. Since the use of SSL has a significant overhead, using it sparingly will improve system performance.
There is more to partitioning than SSL [BTW, I’d argue SSL has “a significant overhead” – it does, but less than significant ].
Partitioning to folders fosters authorizing users based on folder names vs. particular pages. Authorizing users to particular pages is bug prone and less secure practice – imagine if someone just copy and pastes SuperSecurePage.aspx to ‘Copy of SuperSecurePage.aspx’ while you authorization configuration was as follows [very bad practice]:
Suddenly an attacker has free access to sensitive logic at ‘Copy of SuperSecurePage.aspx’ without being authorized.
Concentrate restricted pages into folders and authorized on per folder basis, not per page basis.
Another aspect of partitioning the application in separate folders relates to caching, which is next.
The summary of my authentication and authorization strategy is:
- Rely on the fact that whatever the implementation comes along it is based on IPrincipal, and IIdentity located in Http.Context.Current.User.
When it comes to caching in distributed web application there are four different types of caching come to mind:
- Browser caching. Cached content being stored on client machines and managed by browser’s caching mechanism. Proxy server caching is out of scope for this post.
- Output caching. Rendered HTML output by ASP.NET managed by IIS Server and ASP.NET engine on Web Server. Will be discussed in Page Layout (UI) section.
- Application data caching. Data retrieved from data sources such SQL Server and cached by middle tier [either by IIS Web Server, or Dedicated Downstream server] – out of scope at this point. More info here: Caching Architecture Guide for .Net Applications
- Data source caching. Enabling data source such as SQL Server to cache read only data in the data tier. Out of scope for this post. In short, it’s either achieved by scaling up by setting up a big box with 32 GB RAM and up, or scaling out by separating read and write data as described in Scaling Databases part.
The summary of my decisions regarding Browser caching was made:
- Creating separate folders for static content – CSS, IMG, JS. It will help operations system engineering team easily set expiration policies in production which should lead to less HTTP 304’s improving overall user experience and rendering time.
- Allowing anonymous access to the folders other than Restricted as described in previous section. It will reduce HTTP 401 roundtrips improving user experience and rendering time.
More info here ASP.NET Performance: Get Rid of HTTP 401 and HTTP 304.
General guidelines from Chapter 15: Web Application Archetype:
- Do not use exceptions to control the logical flow of your application.
- Do not catch exceptions unless you *must* handle them, you need to strip sensitive information, or you need to add additional information to the exception.
- Design a global error handler to catch unhandled exceptions.
- Display user-friendly messages to end users whenever an error or exception occurs.
- Do not reveal sensitive information, such as passwords, through exception details.
As per How To: Design Exception Management the key considerations of detecting [catching] exceptions are:
- Only catch the exception when you must perform any of the following tasks:
- Gathering exception details for logging.
- Adding relevant and additional information to the exception.
- Cleaning up any resources used in the code block.
- Retrying the operation to recover from the exception.
- Do not catch an exception and allow the exception to propagate up the call stack, if you do not have any task to accomplish.
- Required information will be gathered in global error handler
- I do not plan to add more information.
- Resources will be cleaned as part of try/catch/finally triplet. But main approach will be using *using* clause.
- I do not plan to retry.
The strategy for handling such unhandled exceptions is:
- Gather the required information. I will rely on Web Events to gather the info.
- Log the exception information. I will rely on Health Monitoring mechanism to publish/log exceptions.
- Send any notifications. I will not be doing that. If needed, will route the event to event log and configure it for further actions.
- Perform any clean-up required. I will do it in try/catch/finally triplet or inside using clause.
- Communicate the error information to the user. I will show generic error page without revealing any implementation details.
The summary of the my exception handling strategy:
For now this is what I have in my Visual Studio project regarding exception handling:
protected void Application_Error(object sender, EventArgs e)
Exception ex = Server.GetLastError().GetBaseException();
string message = ex.ToString();
EventsServices.PublishException("GLOBAL ERROR HANDER: " +
Notice EvenstServices and Tracing classes I use – I utilize previously implemented logging mechanism.
Web.Config [make sure you have created GenericErrorPage.htm]:
Logging & Instrumentation
Logging is implemented in previous step. In previous section I have used it to publish and log an exception caught in global error handler. Since it is based on built-in ASP.NET health monitoring the following section needed to be added to Web.Config:
<add name="Exception Event"
<add name="Exception" eventName="Exception Event"
It reads as follows:
- There is a custom event of type “WebEvent.ExceptionEvent”
- I map it to a friendly name “Exception Event”.
- I route this event to Event Log [notice provider attribute].
Anytime a code similar to the following code runs:
I should observe it in Event Log. Here an example for division by zero caught by global error handler:
Instrumentation is performed by Tracing.TraceMessage(message). I have already used it in global error handler [look in the example above]. The results it produces observed using DebugView:
Other categories of Web Application frame will be discussed in the next part. Stay tuned.