• 构建安全的数据访问组件


    面的代码显示了 CheckProductStockLevel 方法(用来在产品数据库中查询库存量)的示例实现,该代码阐释了本模块前面介绍的数据访问代码的许多重要安全功能。

    using System;
    using System.Data;
    using System.Data.SqlClient;
    using System.Text.RegularExpressions;
    using System.Collections.Specialized;
    using Microsoft.Win32;
    using DataProtection;
    
    public static int CheckProductStockLevel(string productCode)
    {
    int quantity = 0;
    // (1) 由 try/catch 块保护的代码
    try
      {
    // (2) 使用正则表达式验证的输入内容
    //     应该从资源程序集中检索错误消息,以帮助实现
    //     本地化。为简短起见,省略了 Localization(本地化)代码。
    if (Regex.IsMatch(productCode, "^[A-Za-z0-9]{12}$") == false)
    throw new ArgumentException("Invalid product code" );
    //(3) using 语句确保连接被断开
    using (SqlConnection conn = new SqlConnection(GetConnectionString()))
        {
    // (4) 使用参数化存储过程可以应对
    //     SQL 注入攻击
    SqlCommand cmd = new SqlCommand("spCheckProduct", conn);
    cmd.CommandType = CommandType.StoredProcedure;
    
    // 对参数的类型进行检查
    SqlParameter parm = 
    cmd.Parameters.Add("@ProductCode", 
    SqlDbType.VarChar,12);
    parm.Value = productCode;
    // 定义输出参数
    SqlParameter retparm = cmd.Parameters.Add("@quantity", SqlDbType.Int);
    retparm.Direction = ParameterDirection.Output;
    conn.Open();
    cmd.ExecuteNonQuery();
    quantity = (int)retparm.Value;
        }
      }
    catch (SqlException sqlex)
      {
    // (5) 记录完整的异常详细信息。一般(安全的)错误消息
    //     基于 SQL 错误代码返回调用方
    //     为清楚起见,省略了日志和错误标识代码
    throw new Exception("Error Processing Request");
      }
    catch (Exception ex)
      {
    // 记录完整的异常详细信息
    throw new Exception("Error Processing Request");
      }
    return quantity;
    }
    
    // (6) 将加密的数据库连接字符串存保留在注册表中
    private static string GetConnectionString()
    {
    // 从注册表中检索密码文本;进程帐户必须
    // 由注册表项的 ACL 授予“读取”访问权限
    string encryptedString = (string)Registry.LocalMachine.OpenSubKey(
    @"Software\OrderProcessing\")
    .GetValue("ConnectionString");
    // 使用托管的 DPAPI helper 库对该字符串进行解密
    DataProtector dp = new DataProtector(DataProtector.Store.USE_MACHINE_STORE);
    byte[] dataToDecrypt = Convert.FromBase64String(encryptedString);
    return Encoding.ASCII.GetString(dp.Decrypt(dataToDecrypt,null));
    }

    上面的代码显示出下列安全特征(由注释行中的数字进行标识)。

    1.数据访问代码放在 try/catch 块中。这是防止在出现异常时将系统级信息返回到调用方所必需的。调用 ASP.NET Web 应用程序或 Web 服务会处理异常,并向客户端返回适当的一般错误消息,但是数据访问代码不依赖这些消息。

    2.使用正则表达式验证输入。检查所提供的产品 ID,以便验证它只包含 A–Z 和 0–9 范围内的字符,而且不超过 12 个字符。这是旨在防止 SQL 注入攻击的一组对策中的第一个。

    3.SqlConnection 对象是在 Microsoft Visual C#® using 语句的内部创建的。这可确保无论是否发生了异常,都断开方法内部的连接。这会降低拒绝服务攻击的威胁,该威胁尝试使用到数据库的所有可用连接。通过使用 finally 块可以实现类似的功能。

    4.参数化存储过程用于数据访问。这是防止 SQL 注入的另一个对策。

    5.不向客户端返回详细的错误信息。记录异常详细信息,以便帮助诊断问题。

    6.加密的数据库连接字符串存储在注册表中。存储数据库连接字符串最安全的方法之一是,使用 DPAPI 加密该字符串,并将加密的密码文本存储在具有受限 ACL 的受保护的注册表项中。(例如,使用“管理员:完全控制”和“ASP.NET 或企业服务进程帐户:读取”,具体情况取决于由哪个进程托管该组件。)

    注意 该代码显示了如何从注册表检索连接字符串,然后使用托管的 DPAPI helper 库对其进行解密。此库在“Microsoft patterns & practices Volume I, Building Secure ASP.NET Web Applications: Authentication, Authorization, and Secure Communication”的“How To”部分中的 How To: Create a DPAPI Library (英文)中提供。

    学习的路上,分享的知识有不当的地方,希望大家指出。 感谢大家的阅读,希望这些分享能够给您带来帮助。
  • 相关阅读:
    样式超出设定宽度显示显示省略号
    客户端存在潜在危险request.from
    MenuStrip如何设置快捷键
    SVN的使用方法
    长串英文字符不换行的解决办法
    thickbox使用
    System.Web.UI.UserControl”,因此此处不允许
    C#中实现拖动无边框Form窗体和窗体的起始位置
    (转)Altera Forum精彩问答汇总
    (转)如何以32 bit的方式存取SDRAM?
  • 原文地址:https://www.cnblogs.com/lanchong/p/1791454.html
Copyright © 2020-2023  润新知