AppSec Blog

ASP.NET MVC: Audit Logging

Guest Editor: Today's post is from Taras Kholopkin. Taras is a Solutions Architect at SoftServe, Inc. In this post, Taras will take a look at creating an audit logging action filter in the ASP.NET MVC framework.

Audit logging is a critical step for adding security to your applications. Often times, audit logs are used to trace an attacker's steps, provide evidence in legal proceedings, and used to detect and prevent attacks as they are occurring. If you're not convinced yet, many regulatory compliance laws, such as HIPAA, also require security-specific audit logs to be kept. With that said, let's take a look at some high-level things to consider as you build out your audit logging functionality.

Events to Log:
The first step is deciding which events require logging. While regulatory compliance laws, such as HIPAA and PCI, may specify exactly which actions should be logged, each application is different. Here are some general actions to consider logging in your applications:

  • Administrative actions, such as adding, editing, and deleting user accounts / permissions.
  • Authentication attempts
  • Authorized and unauthorized access attempts
  • Reading or writing to sensitive information, such as HIPAA, PCI, encryption keys, authentication tokens, API keys, etc.
  • Validation failures that could indicate an attack against the application

Details to Log:
Each audit log entry must provide enough information to identify the user and their action in the application. The following details provide some details to consider:

  • Timestamp identifying when was an operation/action performed?
  • Who performed the action.? This is often achieved with a unique user identifier, session token hash value, and source IP address.
  • What operation/action was performed? Include the URL, action / method, and action result.
  • Which record(s) were impacted?
  • What values were changed?
  • Determine an event severity level to assist the operations team with incident response

Threshold Monitoring
In addition to the above, it is also helpful to monitor the application for unusual activity. Capturing high volumes of requests beyond normal usage can help detect automated attacks, such as password guessing, SQL injection, or access control attempts. Blocking IP addresses performing unusual activity and notifying the operations team will go a long way towards incident detection and response.

Creating an Audit Logging Action Filter
Now that we know what to do, let's discuss how using ASP.NET MVC. The framework provides a centralized feature, known as Action Filters, which can be used to implement audit logging in your applications. ASP.NET Action Filters are custom classes that provide both declarative and programmatic means to add pre-action and post-action behavior to controller action methods. More information about Action Filters may be found here.

Let's create an example action filter and apply it to the AccountController.Login() and HomeController.Contact() methods from the SecureWebApp project that was described in the previous article ASP.NET MVC: Data Validation Techniques.


First, create a new class called AuditLogActionFilter.cs, as shown in the following code snippet:

using System.Linq; using System.Web; using System.Web.Mvc; using System.Web.Routing; namespace SecureWebApp.Audit { public class AuditLogActionFilter : ActionFilterAttribute { public string ActionType { get; set; } public override void OnActionExecuting(ActionExecutingContext filterContext) { // Retrieve the information we need to log string routeInfo = GetRouteData(filterContext.RouteData); string user = filterContext.HttpContext.User.Identity.Name; // Write the information to "Audit Log" Debug.WriteLine(String.Format("ActionExecuting - {0} ActionType: {1}; User:{2}" , routeInfo, ActionType, user), "Audit Log"); base.OnActionExecuting(filterContext); } public override void OnActionExecuted(ActionExecutedContext filterContext) { // Retrieve the information we need to log string routeInfo = GetRouteData(filterContext.RouteData); bool isActionSucceeded = filterContext.Exception == null; string user = filterContext.HttpContext.User.Identity.Name; // Write the information to "Audit Log" Debug.WriteLine(String.Format("ActionExecuted - {0} ActionType: {1}; Executed successfully:{2}; User:{3}" , routeInfo, ActionType, isActionSucceeded, user), "Audit Log"); base.OnActionExecuted(filterContext); } private string GetRouteData(RouteData routeData) { var controller = routeData.Values["controller"]; var action = routeData.Values["action"]; return String.Format("Controller:{0}; Action:{1};", controller, action); } } }

The newly created custom action filter AuditLogActionFilter overrides OnActionExecuting() and OnActionExecuted() methods to write audit information to debug output window (for testing purposes). In a real scenario, use a logging framework (e.g. nLog, log4net, etc.) to write the details to a secure audit logging repository.

It's very easy to apply the logging filter to the Login() method using our new annotation. With just one line of code, add the AuditLogActionFilter with a custom ActionType argument.
[HttpPost] [AuditLogActionFilter(ActionType="UserLogIn")] [AllowAnonymous] [ValidateAntiForgeryToken] public async Task Login(LoginViewModel model, string returnUrl) {

And to the Contact() method:
[AuditLogActionFilter(ActionType="AccessContactInfo")] public ActionResult Contact() {

The ActionType parameter is just an example showing that it's possible to pass data into the action filter. In this particular case, the ActionType identifies the action being taken for the audit log. Once the value is passed to the action filter, any logic can be added to handle special cases.

When debugging the application, logging in and visiting the contact page cause the following lines to be written to the console output.
Audit Log: ActionExecuting - Controller:Account; Action:Login; ActionType: UserLogIn; User: Audit Log: ActionExecuted - Controller:Account; Action:Login; ActionType: UserLogIn; Executed successfully:True; User: Audit Log: ActionExecuting - Controller:Home; Action:Contact; ActionType: AccessContactInfo; Audit Log: ActionExecuted - Controller:Home; Action:Contact; ActionType: AccessContactInfo; Executed successfully:True;

This shows how easy it is to create a custom-logging filter and apply it to important security-specific actions in your applications.

To learn more about securing your .NET applications, sign up for DEV544: Secure Coding in .NET!

About the author
Taras Kholopkin is a Solutions Architect at SoftServe, and a frequent contributor to the SoftServe United blog. Taras has worked more than nine years in the industry, including extensive experience within the US IT Market. He is responsible for software architectural design and development of Enterprise and SaaS Solutions (including Healthcare Solutions).

Post a Comment


* Indicates a required field.