使用 ActionFilters 在 ASP.NET MVC 中创建高级审计跟踪





5.00/5 (5投票s)
如何在 ASP.NET 中使用 ActionFilters 创建高级审计跟踪
引言
正如我在上一篇关于“使用 ASP.NET MVC ActionFilters 实现简单审计跟踪”的文章中提到的,在处理任何涉及安全和保密性的系统时,问责制是如此重要的一个因素。在那篇文章中,我们讨论了创建一个非常基本的 [Audit]
属性,该属性可用于修饰您的某些控制器和方法,将一些基本的请求信息存储在数据库中,以作为非常简单的审计跟踪。
在这篇后续文章中,我们将实现一些附加功能来充实原始实现,并着眼于存储一些更相关的信息,通过将请求字符串的一些值序列化为 JSON 字符串,然后存储在我们的数据库中,监控用户“Session
”以允许我们跟踪单个用户在其 Session
生命周期内的可审计操作,以及其他一些有趣的功能。
问题
上一篇文章中提到的基本审计功能仅记录了当前用户、他们的 IP 地址以及他们正在访问的控制器操作,但这不足够。我们需要能够指定我们希望在审计日志中存储的附加数据有多细粒度,以便审计消息更有意义。
解决方案
为了实现这一点,我们将对现有的审计 ActionFilter
进行几项修改,并添加以下主要功能:
- Session 捕获 - 我们将实现一个功能,该功能将在特定用户
Session
的所有审计消息之间建立关联(从登录到注销或 Session 过期)。 - 细粒度数据存储 - 我们将添加一个附加功能,该功能将允许我们指定要存储的值的
object
有多细粒度。这将从第一篇文章中提到的非常简单的审计到序列化整个Request
对象的内容。 - JSON 请求序列化 - 我们将使用 JSON 格式来序列化
Request
对象,并且它将与我们进行序列化Request
对象的深度相关。
因此,我们将需要在我们之前的 Audit
类中添加一些字段,以便它能够处理一些扩展功能。
public class Audit
{
//A new SessionId that will be used to link an
//entire users "Session" of Audit Logs
//together to help identifier patterns involving erratic behavior
public string SessionID { get; set; }
public Guid AuditID { get; set; }
public string IPAddress { get; set; }
public string UserName { get; set; }
public string URLAccessed { get; set; }
public DateTime TimeAccessed { get; set; }
//A new Data property that is going to store JSON string
//objects that will later be able to be
//deserialized into objects if necessary to view details about a Request
public string Data { get; set; }
public Audit(){}
}
这两个新属性将足以处理我们计划添加的所有扩展功能。
Session 捕获
Session 捕获将非常基础,我们将使用一些先前在关于 ActionFilters 的另一篇文章中使用过的逻辑,作为识别特定用户的一种非常粗略的方法,基于 IP 地址和 User Agent 等因素来生成一个字符串来生成 MD5 哈希,作为我们的 Session
标识符。
然而,由于我们在“安全”区域内专注于审计,我们将假设当前用户已登录并已通过身份验证。这将允许我们使用他们的 FormsAuthentication
cookie 来播种我们的 MD5 哈希,并在其“Session
”的整个生命周期中作为他们的 Session
标识符。
var sessionIdentifier = string.Join("", MD5.Create().ComputeHash
(Encoding.ASCII.GetBytes(request.Cookies[FormsAuthentication.FormsCookieName].Value)).Select
(s => s.ToString("x2")));
使用 MD5 哈希来实现这一点并非完全必要,因为身份验证 Cookie 名称可能已经足够独特,但我提到这一点是因为您可能希望使用其他值来生成哈希本身。
细粒度数据存储
我认为,对于特定操作在审计方面的“权重”应该比其他操作更重。因此,您可能希望存储有关处理 POST
事件中非常敏感且易出错的数据的操作的更多信息,而不是仅仅查看记录。
这就是为什么我们将添加一个灵活的系统来确定 Request
对象中的哪些信息应该被序列化,使用一个非常简单的数字系统。
- 审计级别 0(无序列化)- 不存储实际的 JSON 请求数据。(对象上的 IPAddress 和其他相关属性仍然可用,但不会有任何额外信息)
- 审计级别 1(轻度序列化)- 存储更多
Request
信息,例如Request
中的 Cookies、Headers 和 Files。 - 审计级别 2(自定义序列化)- 这会捕获所有上述内容,并添加
Form
对象、QueryString
集合以及Request
中传递的所有参数。您可以根据您处理的内容自由定制此级别或任何其他级别。 - 审计级别 3(完整请求序列化)- (序列化所有可序列化字段将在以后的文章中介绍,或者留给用户练习)
我们将用于添加这些功能的实际代码将是我们的 AuditAttribute
类中的一个属性。
public int AuditingLevel { get; set; }
以及一个将在属性内部用于应用适当序列化的方法。
//This will serialize the Request object based on the level that you determine
private string SerializeRequest(HttpRequestBase request)
{
switch (AuditingLevel)
{
//No Request Data will be serialized
case 0:
default:
return "";
//Basic Request Serialization - just stores Data
case 1:
return Json.Encode(new { request.Cookies, request.Headers, request.Files});
//Middle Level - Customize to your Preferences
case 2:
return Json.Encode(new { request.Cookies, request.Headers,
request.Files, request.Form, request.QueryString, request.Params});
//Highest Level - Serialize the entire Request object
//(As mentioned earlier, this will blow up)
case 3:
//We can't simply just Encode the entire request
//string due to circular references as well
//as objects that cannot "simply" be
//serialized such as Streams, References etc.
return Json.Encode(request);
}
}
您可以看到序列化部分非常简单,可以通过向要序列化的匿名对象添加其他属性来轻松修改以处理其他自定义,所以尽情发挥吧。
装饰您的方法(适度)
由于 Auditing
属性有一个可公开访问的属性来指定我们的序列化和审计级别,因此它将允许您轻松、干净地装饰控制器操作和整个控制器。
这项附加功能不仅需要您编写很少的代码,而且增加了巨大的灵活性,因此您可以为需要密切监控的重要操作使用更高的审计级别,如下所示。
[Audit]
public ActionResult Unimportant() { ... }
[Audit(AuditingLevel = 1)]
public ActionResult SlightlyImportant() { ... }
[Audit(AuditingLevel = 2)]
public ActionResult Important() { ... }
[Audit(AuditingLevel = 3]
public ActionResult Classified() { ... }
这就是确定哪些操作将被“审计”以及将应用何种级别的基本内容。
对于这个非常基本的示例,只需将鼠标悬停在 Data
对象上,您就可以看到其中隐藏的一些值。(这仅用于示例目的,您可以随意按照您认为合适的方式格式化此区域。)
将此视为起点
本文和上一篇文章中提到的所有示例仅仅是您可以在审计场景中使用 Action Filters 的一些可能性的想法。目前它远非一个完整的系统,但对于那些希望为其代码库创建功能齐全的审计系统或安全框架的人来说,它应该是一个很好的起点。
您可以下载整个示例来随意修改,我希望它能为提高您当前和未来应用程序的问责制和安全性提供基础。