3

I create an ASP.Net MVC 5 web application whose service can be used by anonymous users. When the anonymous user uses the web service, it will do some query from the database. Due to security reason, however, my client wants to track "suspicious" activities of anonymous users. One of them includes how many times an anonymous user queries per day (for preventing significant amount of data being "stolen").

Is there any way to capture this information?

For registered user, we could create additional property in the ApplicationUser called QueryNo and add it in the Claim like this:

public class ApplicationUser : IdentityUser {
    public uint QueryNo { get; set; } //how many times this user has queried

    public async Task<ClaimsIdentity> GenerateUserIdentityAsync(UserManager<ApplicationUser> manager) {
        // Note the authenticationType must match the one defined in CookieAuthenticationOptions.AuthenticationType
        var userIdentity = await manager.CreateIdentityAsync(this, DefaultAuthenticationTypes.ApplicationCookie);
        // Add custom user claims here
        userIdentity.AddClaim(new Claim("QueryNo", QueryNo));
        return userIdentity;
    }
}

And when we want to track its activity, we could simply increase its QueryNo per query activity. When we want to display it, we could, for example, simply define an extension for Identity like this:

public static class IdentityExtensions {
    public static string GetQueryNo(this IIdentity identity) {
        if (identity == null) {
            throw new ArgumentNullException("identity");
        }
        var ci = identity as ClaimsIdentity;
        if (ci != null) {
            return ci.FindFirstValue("QueryNo");
        }
        return null;
    }
}

And then simply use it in the view like this:

<p>No Of Query: @User.Identity.GetQueryNo()</p>

But how do we track anonymous user's activities (i.e. such as its no of queries)?

Ian
  • 30,182
  • 19
  • 69
  • 107
  • I don't think you can have a totally bulletproof solution. You can track via the user's session - which someone malicious could get around simply by disabling cookies. If you're ok with that, I'd think an action filter that tracks which actions are consumed with particular parameters might be your best bet. You can capture things like the session ID, IP address, browser details, etc... though all those could be suspect. – Mister Epic Aug 10 '16 at 12:20
  • Disabling cookies would be troublesome, certainly. :/ but let's assume most of the users don't... mind to elaborate/give example of your suggestion, "I'd think an action filter that tracks which actions are consumed with particular parameters might be your best bet. You can capture things like the session ID, IP address, browser details, etc... though all those could be suspect"? – Ian Aug 10 '16 at 12:27

1 Answers1

6

First create your action filter:

public class TrackingActionFilter : ActionFilterAttribute
{

    public override void OnActionExecuting(ActionExecutingContext filterContext)
    {
        var sessionId = filterContext.HttpContext.Session.SessionID;
        Debug.WriteLine("Printing session Id: " + sessionId);

        var ip = filterContext.HttpContext.Request.UserHostAddress;
        Debug.WriteLine("Printing ip: " + ip);

        var headers = filterContext.RequestContext.HttpContext.Request.Headers;
        foreach(var header in headers) {
            Debug.WriteLine("Printing header: " + header);
        }

        var parms = filterContext.HttpContext.Request.Params;
        foreach (var key in parms.AllKeys)
        {
            Debug.WriteLine("Printing parameter: " + key + " - " + parms[key]);
        }

        var routeDataKeys = filterContext.RouteData.Values.Keys;
        foreach(var key in routeDataKeys)
        {
            Debug.WriteLine("Printing route data value: " + key + " - " + filterContext.RouteData.Values[key]);
        }

        //Stolen with love from http://stackoverflow.com/questions/12938621/how-can-i-log-all-query-parameters-in-an-action-filter
        var stream = filterContext.HttpContext.Request.InputStream;
        var data = new byte[stream.Length];
        stream.Read(data, 0, data.Length);
        Debug.WriteLine(Encoding.UTF8.GetString(data));
    }
}

Obivously you would capture the relevant details instead of just writing them to the debug window.

Now you can apply the action filter at the action level:

[TrackingActionFilter]
public ActionResult Index()

Or at the controller level:

[TrackingActionFilter]
public class HomeController : Controller

Or you can cover your entire MVC application globally via the FilterConfig:

public class FilterConfig
{
    public static void RegisterGlobalFilters(GlobalFilterCollection filters)
    {
        filters.Add(new HandleErrorAttribute());
        filters.Add(new TrackingActionFilter());
    }
}
Mister Epic
  • 16,295
  • 13
  • 76
  • 147