• 构建安全的数据访问异常管理(八)


    异常条件可能会由配置错误、代码中的错误或恶意输入引起。如果没有正确的异常管理,这些条件可能会透露有关数据源位置和特性的敏感信息,以及有价值的连接详细信息。下面的建议适用于数据访问代码:

    •捕获和记录 ADO.NET 异常。

    •确保数据库连接总是处于断开状态。

    •在 ASP.NET 应用程序中使用一般错误页面。

    捕获和记录 ADO.NET 异常

    将数据访问代码放在 try/catch 块中并处理异常。在编写 ADO.NET 数据访问代码时,由 ADO.NET 生成的异常类型取决于数据提供程序。例如:

    •SQL Server .NET Framework 数据提供程序生成 SqlException

    •OLE DB .NET Framework 数据提供程序生成 OleDbException

    •ODBC .NET Framework 数据提供程序生成 OdbcException

    捕获异常

    下面的代码使用 SQL Server .NET Framework 数据提供程序,并显示应该如何捕获类型为 SqlException 的异常。

    try
    {
    // 数据访问代码
    }
    catch (SqlException sqlex) // 比较具体
    {
    }
    catch (Exception ex) // 比较一般
    {
    }
    记录异常

    还应该记录来自 SqlException 类的详细信息。此类公开那些包含异常条件详细信息的属性。这些属性包括 Message 属性(用来描述错误)、Number 属性(用来唯一标识错误类型)以及 State 属性(其中包含其他信息)。State 属性通常用来指示特定错误条件出现的具体位置。例如,如果某个存储过程从多个行中生成同一错误,则 State 属性可以指出错误出现的具体位置。最后,Errors 集合中包含 SqlError 对象,这些对象提供详细的 SQL 服务器错误信息。

    下面的代码片断显示了如何通过使用 SQL Server .NET Framework 数据提供程序来处理 SQL Server 错误条件:

    using System.Data;
    using System.Data.SqlClient;
    using System.Diagnostics;
    
    // 由数据访问层 (DAL) 组件公开的方法
    public string GetProductName( int ProductID )
    {
    SqlConnection conn = new SqlConnection(
    "server=(local);Integrated Security=SSPI;database=products");
    // 将所有的数据访问代码包含在 try 块中
    try
      {
    conn.Open();
    SqlCommand cmd = new SqlCommand("LookupProductName", conn );
    cmd.CommandType = CommandType.StoredProcedure;
    
    cmd.Parameters.Add("@ProductID", ProductID );
    SqlParameter paramPN = 
    cmd.Parameters.Add("@ProductName", SqlDbType.VarChar, 40 );
    paramPN.Direction = ParameterDirection.Output;
    
    cmd.ExecuteNonQuery();
    // 在该方法返回之前先执行 finally 代码
    return paramPN.Value.ToString();  
      }
    catch (SqlException sqlex)
      {
    // 处理数据访问异常条件
    // 记录具体的异常详细信息
    LogException(sqlex);
    // 将当前异常包装在一个相关性更强的
    // 外部异常中,并重新引发新异常
    throw new Exception(
    "Failed to retrieve product details for product ID: " + 
    ProductID.ToString(), sqlex );
      }
    finally
      {
    conn.Close(); // 确保连接处于断开状态
      }
    }
    
    // Helper 例程,该例程将 SqlException 详细信息记录到
    // 应用程序事件日志中
    private void LogException( SqlException sqlex )
    {
    EventLog el = new EventLog();
    el.Source = "CustomAppLog";
    string strMessage;
    strMessage = "Exception Number :" + sqlex.Number + 
    "(" + sqlex.Message + ") has occurred";
    el.WriteEntry( strMessage );
    
    foreach (SqlError sqle in sqlex.Errors)
      {
    strMessage = "Message:" + sqle.Message +
    " Number:" + sqle.Number +
    " Procedure:" + sqle.Procedure +
    " Server:" + sqle.Server +
    " Source:" + sqle.Source +
    " State:" + sqle.State +
    " Severity:" + sqle.Class +
    " LineNumber:" + sqle.LineNumber;
    el.WriteEntry( strMessage );
      }
    }
    确保数据库连接总是处于断开状态

    如果发生异常,一定要断开数据库连接,并释放其他所有受限制的资源。使用 finally 块或 C# using 语句,可以确保无论是否发生了异常条件,都会断开数据库连接。上面的代码阐释了 finally 块的用法。还可以按如下方式使用 C# using 语句:

    using ((SqlConnection conn = new SqlConnection(connString)))
    {
    conn.Open();
    // 在以下情况下将断开连接:生成异常或者控制流
    // 通常会离开 using 语句的使用范围
    }
    在 ASP.NET 应用程序中使用一般错误页面

    如果您的数据访问代码由 ASP.NET Web 应用程序或 Web 服务调用,则应该对 <customErrors> 元素进行配置,以防异常详细信息传播回到最终用户。还可以通过使用该元素来指定一般错误页面,如下所示。

    <customErrors mode="On" defaultRedirect="YourErrorPage.htm" />

    对于生产服务器设置 mode="On"。只有在发布之前开发和测试软件时才使用 mode="Off"。如果不这样做,将导致向最终用户返回大量错误信息(如图 14.4 中显示的信息)。这些信息可能包含数据库服务器的名称、数据库名称和连接凭据。

    详细的异常信息会透露敏感数据

    图 14.4
    详细的异常信息会透露敏感数据

    图 14.4 还显示了数据访问代码中接近导致异常的行的大量漏洞。特别是:

    •连接字符串是硬编码的。

    •特权极高的 sa 帐户用于连接到数据库。

    sa 帐户有一个弱密码。

    •SQL 命令的构造容易受到 SQL 注入攻击;输入内容未进行验证,代码不使用参数化存储过程。

  • 相关阅读:
    linux less命令用法
    Spark-RDD 模型 以及运行原理
    Spark 架构原理介绍 以及 job、task、stag 概念
    Kafka 基本概念以及框架介绍
    负载均衡- TCP/ IP 基础知识
    JAVA多线程和并发基础面试题
    Java并发使用--线程池
    Java基础--并发编程
    事务实现原则(一) 事务的改变以及JDBC事务的设计
    Spark RDD Transformation和Action
  • 原文地址:https://www.cnblogs.com/lanchong/p/1791453.html
Copyright © 2020-2023  润新知