Skip to content

Latest commit

 

History

History
428 lines (319 loc) · 18.8 KB

File metadata and controls

428 lines (319 loc) · 18.8 KB

十一、记录和监控不足

对 ASP.NET Core web 应用的攻击可能随时发生。开发人员必须授权其安全团队通过从 web 应用生成足够的日志来重构事件。记录正确的信息将有助于确定事件的详细信息,并为审计目的识别关键数据。未能记录关键安全信息的缺点是,安全团队无法生成正确的分析或报告。但是,过多的日志记录可能会导致敏感数据泄露。只有通过积极监测,才能对此类安全事件采取必要和立即的应对措施。开发人员必须在 ASP.NET Core web 应用生成的日志中启用监视,以实现更实时的防御。

在本章中,我们将介绍以下配方:

  • 修复异常记录不足的问题
  • 修复了数据库DB事务记录不足的问题
  • 修复过多的信息记录
  • 修复缺乏安全监控的问题

在本章结束时,您将了解如何在我们的示例网上银行应用中正确添加适当的异常记录,如何记录关键 DB 事务,如何防止记录过多的数据或信息,以及如何启用安全监控。

技术要求

这本书的编写和设计是为了使用Visual Studio 代码VS 代码)、Git 和.NET Core 5.0。配方中的代码示例主要在 ASP.NET Core Razor 页面中提供。示例解决方案还使用 SQLite 作为 DB 引擎,以简化设置。本章的完整代码示例可在上找到 https://github.com/PacktPublishing/ASP.NET-Core-Secure-Coding-Cookbook/tree/main/Chapter11

修复异常记录不足的问题

安全相关事件,如用户身份验证或启用和禁用双因素身份验证2FA)-当发生时,必须记录并跟踪。为了了解安全事件发生时的事件顺序,这些事件对于审核是必不可少的。

在此配方中,我们将利用 ASP.NET Core 的内置日志提供程序修复安全相关异常的日志记录不足问题。

准备好了吗

对于本章的食谱,我们需要一个在线银行应用示例。

打开命令 shell 并通过克隆ASP.NET-Core-Secure-Coding-Cookbook存储库下载示例网银应用,如下所示:

git clone https://github.com/PacktPublishing/ASP.NET-Core-Secure-Coding-Cookbook.git

运行示例应用以验证没有生成或编译错误。在命令 shell 中,导航到位于\Chapter11\insufficient-logging-exception\before\OnlineBankingApp的示例应用文件夹,并运行以下命令:

dotnet build

dotnet build命令将构建示例OnlineBankingApp项目及其依赖项。

怎么做…

让我们来看看这个食谱的步骤:

  1. 在开始练习文件夹中,通过键入以下命令启动 VS 代码:

    code .
  2. 打开Areas\Identity\Pages\Account\Manage\Disable2fa.cshtml.cs并注意OnGet

    public async Task<IActionResult> OnGet()
    {
        var customer = await         _customerManager.GetUserAsync(User);
        if (customer== null)
        {
        return NotFound($"Unable to load customer with         ID '{_ customerManager.GetUserId(User)}'.");
        }
        if (!await _customerManager         .GetTwoFactorEnabledAsync(customer))
        {
     throw new InvalidOperationException($"Cannot             disable 2FA for customer with ID                 '{_customerManager.GetUserId(User)}'                    as it's not currently enabled.");
        }
        return Page();
    }

    下的代码

  3. Also, notice the code under the OnPostAsync method:

    public async Task<IActionResult> OnPostAsync()
    {
        var customer = await         _customerManager.GetUserAsync(User);
        if (customer == null)
        {
            return NotFound($"Unable to load customer with            ID '{_customerManager.GetUserId(User)}'.");
        }
        var disable2faResult = await         _customerManager.SetTwoFactorEnabledAsync             (customer, false);
        if (!disable2faResult.Succeeded)
        {
     throw new InvalidOperationException         ($"Unexpected error occurred disabling 2FA for             customer with ID '{_customerManager                 .GetUserId (User)}'.");
        }
        _logger.LogInformation("Customer with ID         '{UserId}' has disabled 2fa.",            _customerManager.GetUserId(User));
        StatusMessage = "2fa has been disabled. You can         reenable 2fa when you setup an authenticator             app";
        return         RedirectToPage("./TwoFactorAuthentication");
    }

    这两种方法都有一行代码,其中抛出了一个InvalidOperationException异常。异常表示尝试禁用 2FA 但失败。应将这些事件视为异常并记录。这些事件可视为异常,应记录每一个事件。

  4. 为了修复事件日志记录的不足,我们重构了OnGet方法,并在抛出InvalidOperationException异常时添加日志记录:

    public async Task<IActionResult> OnGet()
    {
        var customer = await         _customerManager.GetUserAsync(User);
        if (customer == null)
        {
            return NotFound($"Unable to load customer with            ID '{_customerManager.GetUserId(User)}'.");
        }
        if (!await         _customerManager.GetTwoFactorEnabledAsync             (user))
        {
     _logger.LogError($"Cannot disable 2FA for             customer with ID '{_customerManager                 .GetUserId(User)}' as it's not                     currently enabled.");
            throw new InvalidOperationException($"Cannot             disable 2FA for customer with ID                 '{_customerManager.GetUserId(User)}'                    as it's not currently enabled.");
        }
        return Page();
    }
  5. We also refactor the OnPostAsync method, as shown here:

    public async Task<IActionResult> OnPostAsync()
    {
        var customer = await         _customerManager.GetUserAsync(User);
        if (customer == null)
        {
            return NotFound($"Unable to load customer with            ID '{_customerManager.GetUserId(User)}'.");
        }
        var disable2faResult = await         _customerManager.SetTwoFactorEnabledAsync             (customer, false);
        if (!disable2faResult.Succeeded)
        {
     _logger.LogError($"Unexpected error occurred             disabling 2FA for customer with ID                 '{_customerManager.GetUserId                     (User)}'.");
            throw new InvalidOperationException             ($"Unexpected error occurred disabling 2FA                 for customer with ID'{_customerManager                     .GetUserId(User)}'.");
        }
        _logger.LogInformation("Customer with ID         '{UserId}' has disabled 2fa.",            _customerManager.GetUserId(User));
        StatusMessage = "2fa has been disabled. You can         reenable 2fa when you setup an authenticator             app";
        return         RedirectToPage("./TwoFactorAuthentication");
    }

    我们使用依赖注入DI中的_logger对象调用LogError方法,该方法在当前日志提供程序中写入错误日志。

它是如何工作的…

我们已通过调用ConfigureLogging方法预先配置了我们的示例网上银行应用,以添加 Windows 事件日志记录。ConfigureLogging方法将为 WindowsEventLog提供程序创建一个ILogger对象。我们将ILogger对象的SourceName属性设置为OnlineBankingApp,以识别样本网上银行应用生成的日志:

public static IHostBuilder CreateHostBuilder(string[] args) =>
    Host.CreateDefaultBuilder(args).ConfigureLogging(logging =>
 {
 logging.AddEventLog(eventLogSettings =>
 {
 eventLogSettings.SourceName =                    "OnlineBankingApp"; 
 });
 })

我们还通过在appsettings.json文件的Logging部分添加一个条目来配置信息日志记录。这将创建一个日志级别设置为InformationOnlineBankingApp类别:

{
  "Logging": {
    "EventLog": {
      "LogLevel": {
        "Default": "Warning",
 "OnlineBankingApp": "Information"
      }
    },    

有了这些设置,我们现在可以使用 WindowsEventLog提供程序进行日志记录。ILogger对象的实例_logger已经通过 DI 提供。我们现在只需在发生严重异常的代码行中调用ILogger对象的LogError方法。

笔记

需要注意的是,日志的位置和存储方式是基本标准。配置或代码不应将日志放置在与 web 服务器相同的位置。我们必须实施适当的访问控制,以防止未经授权查看日志。有一些开源和企业安全解决方案提供了安全查看、收集和存储日志的工具。

修复数据库事务记录不足的问题

基本数据库事务如创建、读取和删除记录对于审计跟踪至关重要,尤其是在执行数据库功能时发生错误时。

在此配方中,我们将修复在引发相关异常时失败的 DB 事务日志记录不足的问题。

怎么做…

让我们来看看这个食谱的步骤:

  1. 在开始练习文件夹中,通过键入以下命令启动 VS 代码:

    code .
  2. Open Pages\Backups\Edit.cshtml.cs and notice a lack of DB operation logging in the OnPostAsync method:

    public async Task<IActionResult> OnPostAsync()
    {
        if (!ModelState.IsValid){
            return Page();
        }
        _context.Attach(Backup).State =        EntityState.Modified;
        try{
            await _context.SaveChangesAsync();
        }
     catch (DbUpdateException){
    if (!BackupExists(Backup.ID)){
     return NotFound();
     }
     else{
     throw;
     }
     }
        return RedirectToPage("./Index");
    }

    但是,应记录数据库操作,如执行备份和更新其相关记录。

  3. 为了修复丢失的高值 DB 事务日志记录,让我们通过 DI 使用ILogger接口添加一个记录器。首先定义类型为ILogger

    public class EditModel : PageModel
    {
        private readonly OnlineBankingApp.Data         .OnlineBankingAppContext _context;
        private readonly ILogger<EditModel> _logger;
    // code removed for brevity

    _logger成员

  4. 接下来,将成员注入EditModel构造函数中:

    public EditModel(OnlineBankingApp.Data     .OnlineBankingAppContext context,        ILogger<EditModel> logger)
    {
        _logger = logger;
        _context = context;
    }
  5. In the try-catch block, add the following lines of code:

    try{
        await _context.SaveChangesAsync();
    }
    catch (DbUpdateException ex){
        if (!BackupExists(Backup.ID)){
    _logger.LogError("Backup not found");
    return NotFound();
    }
    else{
     _logger.LogError($"An error occurred in
     backing up the DB { ex.Message } ");
            throw;
        }
    }

    添加这些代码行将在EventLog日志记录提供程序中使用前面配方中说明的相同日志记录设置写入错误日志。

它是如何工作的…

在敏感数据库操作(如数据库备份)期间,可能会发生意外错误。此类异常可能会在我们的示例网上银行应用中导致系统故障或更糟的攻击。我们通过跟踪这些数据库操作并了解事件发生的时间来降低丢失数据完整性的风险。我们调用LogError函数将日志写入 Windows 事件日志:

if (!BackupExists(Backup.ID)){
    _logger.LogError("Backup not found");
    return NotFound();
}
else{
    _logger.LogError($"An error occurred in backing up the         DB { ex.Message } ");
    throw;
}

作为最佳实践,我们为我们预期的特定异常(提供适当的错误日志消息,最多只记录一般异常的Message属性,避免在我们创建的日志中披露敏感信息(更多关于中异常处理的最佳实践)第 13 章最佳实践)。

修复过多的信息记录

正如我们在第 4 章敏感数据泄露中所了解到的,确保您防止个人信息泄露是确保应用安全的关键,日志信息也是如此。虽然日志很有用,但记录过多数据也有风险。犯罪者会找到获取有用信息的方法,而日志存储是他们试图发现的来源之一。

在此配方中,我们将修复过度记录的信息,如用户名和密码。

怎么做…

让我们来看看这个食谱的步骤:

  1. 在开始练习文件夹中,通过键入以下命令启动 VS 代码:

    code .
  2. 打开Areas\Identity\Pages\Account\Login.cshtml.cs并找到向日志发送过多敏感信息的代码行:

        if (ModelState.IsValid)
        {
            // This doesn't count login failures towards         account lockout
            // To enable password failures to trigger         account lockout, set lockoutOnFailure: true
            var signInResult = await             _signInManager.PasswordSignInAsync                 (Input.Email, Input.Password,                     Input.RememberMe, lockoutOnFailure                         : false);
        if (signInResult.Succeeded)
            {
     _logger.LogInformation($"Customer with                 email { Input.Email } and password                     { Input.Password } logged in");
  3. To fix the issue, we replace the line with a proper log entry:

    if (ModelState.IsValid)
    {
        // This doesn't count login failures towards     account lockout     // To enable password failures to trigger account     lockout, set lockoutOnFailure: true
        var signInResult = await         _signInManager.PasswordSignInAsync             (Input.Email, Input.Password,                 Input.RememberMe, lockoutOnFailure:                    false);
        if (signInResult.Succeeded)
        {
            _logger.LogInformation("User logged in.");
            if (string.IsNullOrEmpty(HttpContext             .Session.GetString(SessionKeyName)))
            {
        HttpContext.Session.SetString         (SessionKeyName, Input.Email);
            }

    重构代码以删除用户名和密码等敏感信息,可防止犯罪者获得日志存储并使用收集到的信息利用我们的示例网上银行应用进行攻击。

它是如何工作的…

我们可以通过Run命令打开 Windows事件查看器来查看我们的示例网银应用生成的日志。以下是执行此操作的步骤:

  1. Type Windows + R, and once the Run window shows up, as shown in the following screenshot, type eventvwr.msc:

    Figure 11.1 – Run command

    图 11.1–运行命令

  2. 事件查看器(本地)中,展开Windows 日志,然后展开应用。查找名称相当于OnlineBankingApp的日志:

Figure 11.2 – Event Viewer

图 11.2–事件查看器

您在事件查看器中看到的信息日志是前面配方中LogInformation方法调用生成的记录。我们修改了代码,以防止对敏感信息(如用户凭据)进行显式.t 日志记录。我们使用一个通用的信息消息来解决暴露细节的问题,我们不希望任何人滥用这些细节。

修复缺乏安全监控的问题

监控允许我们主动观察 ASP.NET Core web 应用中发生的事件。错过实时发生的事件会导致攻击者每分钟造成更大的伤害。开发人员必须在其 ASP.NET Core web 应用中启用监视,以便进行早期预防性检测。

在此配方中,我们将通过实现Azure Application Insights来修复我们示例网上银行 web 应用中缺乏安全监控的问题。

怎么做…

让我们来看看这个食谱的步骤:

  1. 在开始练习文件夹中,通过键入以下命令启动 VS 代码:

    code .
  2. 导航至菜单中的终端****新终端,或在 VS 代码中按Ctrl+Shift+即可。 ** 通过在 VS 代码终端中运行以下命令来安装应用洞察软件开发工具包SDK):

    dotnet add package Microsoft.ApplicationInsights.AspNetCore
    • Open Startup.cs and make a call to the AddApplicationInsightsTelemetry method under ConfigureServices:
    public void ConfigureServices(IserviceCollection     services)
    {
        services.AddApplicationInsightsTelemetry();
    // code removed for brevity

    顾名思义,AddApplicationInsightsTelemetry方法将为我们的样本网上银行应用添加一个 Insights 遥测采集。

    • Open the appsettings.json file to add the instrumentation key:
    {
     "ApplicationInsights": {
     "InstrumentationKey": "My-Instrumentation-Key"
     }, 
      "Logging": {
        "EventLog": {
          "LogLevel": {
            "Default": "Warning",
            "OnlineBankingApp": "Information"
          }
        },    

    笔记

    要生成插装密钥,请按照 Microsoft Azure Monitor 官方在线文档中的创建应用洞察资源说明进行操作,该文档位于https://docs.microsoft.com/en-us/azure/azure-monitor/app/create-new-resource

    • 在终端中键入以下命令以构建并运行示例应用:
    dotnet run
    • Open a browser and go to https://localhost:5001/:.

    传入请求现在将由 Application Insights SDK 收集,包括具有MediumErrorCritical严重性的ILogger日志。*

*## 它是如何工作的…

我们通过将示例网上银行 web 应用与Azure 应用洞察集成来实现遥测采集。Application Insights 收集的不仅仅是我们的ILogger提供商生成的性能指标和日志,它还执行分析和应用安全检测。此基于云的服务在出现安全问题时发送警报和通知,如不安全的表单、不安全的统一资源定位器URL)访问和可疑的用户活动。

还有更多…

配方的前面步骤用于从服务器端启用遥测采集。以下是从客户端启用监视的步骤:

  1. 打开Pages\_ViewImports.cshtml并从 Application Insights SDK 注入JavaScriptSnippet服务:

    @using OnlineBankingApp
    @namespace OnlineBankingApp.Pages
    @addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
    @inject Microsoft.ApplicationInsights.AspNetCore.JavaScriptSnippet JavaScriptSnippet
  2. 打开Pages\Shared\_Layout.cshtml并在head部分插入 JavaScript 片段,同时调用Html.Raw助手方法:

    ...
        <link rel="stylesheet" href="~/lib/bootstrap/        dist/css/bootstrap.min.css" />
        <link rel="stylesheet" href="~/css/site.css" />
     @Html.Raw(JavaScriptSnippet.FullScript)
    </head>

通过在_Layout.cshtml文件中放置 JavaScript,我们可以在使用我们的示例网上银行 web 应用的布局模板的所有页面上启用客户端监控。*