web安全方面的一些小总结
2009-09-17 10:01
260 查看
1、数据访问授权
通过设置访问某些方法的访问角色来控制代码的安全性。代码如下:
代码应该在用户连接数据库之前根据角色或者标识对其授权。角色检查通常用在应用程序的业务逻辑中,但是如果您没有明确地区分业务和数据访问逻辑,则应该在访问数据库的方法上使用主体权限要求。
1)以下属性确保了只有是 Manager 角色成员的用户可调用 DisplayCustomerInfo 方法:
[PrincipalPermissionAttribute(SecurityAction.Demand, Role="Manager")]
public void DisplayCustomerInfo(int CustId)
{
}
2)以下代码片段使用一个显式的、编程实现的角色检查确保调用方是 Manager 角色的成员:
public void DisplayCustomerInfo(int CustId)
{
if(!Thread.CurrentPrincipal.IsInRole("Manager"))
{
. . .
}
}
2、保护敏感数据,通过添加salt 值的密码散列值:
如果您需要实现包含用户名和密码的用户存储区,则不要以明文或者加密的格式存储密码。不要存储密码,而应该存储带附加 salt 的非可逆散列值以降低字典攻击的风险。
注 salt 值是一个强加密的随机数。
创建 salt 值
以下代码说明了如何通过使用 System.Security.Cryptography 命名空间中的 RNGCryptoServiceProvider 类提供的随机数生成功能生成 salt 值。
public static string CreateSalt(int size)
{
RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider();
byte[] buff = new byte[size];
rng.GetBytes(buff);
return Convert.ToBase64String(buff);
}
创建(带 salt 值的)散列值
以下代码片段说明了如何从提供的密码和 salt 值生成散列值。
public static string CreatePasswordHash(string pwd, string salt)
{
string saltAndPwd = string.Concat(pwd, salt);
string hashedPwd =
FormsAuthentication.HashPasswordForStoringInConfigFile(
saltAndPwd, "SHA1");
return hashedPwd;
}
3)捕获和记录 ADO.NET 异常
将数据访问代码置于 try / catch 块中并处理异常。在编写 ADO.NET 数据访问代码时,ADO.NET 所生成的异常类型取决于数据提供程序。例如: • SQL Server .NET Framework 数据提供程序将生成 SqlExceptions。
• OLE DB .NET Framework 数据提供程序将生成 OleDbExceptions。
• ODBC .NET Framework 数据提供程序将生成 OdbcExceptions。
捕获异常
以下代码使用 SQL Server .NET Framework 数据提供程序,并说明了如何捕获 SqlException 类型的异常。
try
{
// Data access code
}
catch (SqlException sqlex) // more specific
{
}
catch (Exception ex) // less specific
{
}
日志记录异常
您还应该将来自 SqlException 类的详细信息记录下来。这个类公开了包含异常情况详细信息的属性。这包括一个说明错误的 Message 属性,一个唯一标识错误类型的 Number 属性,和一个包含其他信息的 State 属性。State 属性通常用来指示特定错误情况的某次出现。例如,如果存储过程在不止一行中出现了同样的错误,State 属性可指示特定的那一次。最后,Errors 集合包含可提供详细 SQL Server 错误信息的 SqlError 对象。
以下代码片段说明了如何通过使用 SQL Server .NET Framework 数据提供程序处理 SQL Server 错误情况:
using System.Data;
using System.Data.SqlClient;
using System.Diagnostics;
// Method exposed by a Data Access Layer (DAL) Component
public string GetProductName( int ProductID )
{
SqlConnection conn = new SqlConnection(
"server=(local);Integrated Security=SSPI;database=products");
// Enclose all data access code within a try block
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();
// The finally code is executed before the method returns
return paramPN.Value.ToString();
}
catch (SqlException sqlex)
{
// Handle data access exception condition
// Log specific exception details
LogException(sqlex);
// Wrap the current exception in a more relevant
// outer exception and re-throw the new exception
throw new Exception(
"Failed to retrieve product details for product ID: " +
ProductID.ToString(), sqlex );
}
finally
{
conn.Close(); // Ensures connection is closed
}
}
// Helper routine that logs SqlException details to the
// Application event log
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 );
}
}
4)在 ASP.NET 应用程序中使用一般性错误页
如果您的数据访问代码是由一个 ASP.NET Web 应用程序或者 Web 服务调用的,应该配置 <customErrors> 元素以防止异常详细信息传回最终用户。您还可以通过使用这个元素指定一般性错误页,如下所示。
<customErrors mode="On" defaultRedirect="YourErrorPage.htm" />
为产品服务器设置 mode="On" 。当您在发布前开发和测试软件时,只能使用 mode="Off"。如果无法做到这一点,将导致返回给最终用户丰富的错误信息,如图 4 中所示。这些信息可能包括数据库服务器名称、数据库名称和连接凭据。
图 4. 详细的异常信息会暴露敏感的数据
图 4 还显示了在导致异常的行附近的数据访问代码中仍然存在的许多缺陷。具体如下: • 连接字符串是硬编码的。
• 使用了高特权 sa 帐户来连接数据库。
• sa 帐户的密码很脆弱。
• SQL 命令构造很容易遭到 SQL 注入攻击,输入没有进行验证,而代码没有使用参数化的存储过程
下面是一个综合的示例演示如何安全操作数据层:
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) Code protected by try/catch block
try
{
// (2) Input validated with regular expression
// Error messages should be retrieved from the resource assembly to help
// localization. The Localization code is omitted for the sake of brevity.
if (Regex.IsMatch(productCode, "^[A-Za-z0-9]{12}$") == false)
throw new ArgumentException("Invalid product code" );
//(3) The using statement ensures that the connection is closed
using (SqlConnection conn = new SqlConnection(GetConnectionString()))
{
// (4) Use of parameterized stored procedures is a countermeasure for
// SQL injection attacks
SqlCommand cmd = new SqlCommand("spCheckProduct", conn);
cmd.CommandType = CommandType.StoredProcedure;
// Parameters are type checked
SqlParameter parm =
cmd.Parameters.Add("@ProductCode",
SqlDbType.VarChar,12);
parm.Value = productCode;
// Define the output parameter
SqlParameter retparm = cmd.Parameters.Add("@quantity", SqlDbType.Int);
retparm.Direction = ParameterDirection.Output;
conn.Open();
cmd.ExecuteNonQuery();
quantity = (int)retparm.Value;
}
}
catch (SqlException sqlex)
{
// (5) Full exception details are logged. Generic (safe) error message
// is thrown back to the caller based on the SQL error code
// Log and error identification code has been omitted for clarity
throw new Exception("Error Processing Request");
}
catch (Exception ex)
{
// Log full exception details
throw new Exception("Error Processing Request");
}
return quantity;
}
// (6) Encrypted database connection string is held in the registry
private static string GetConnectionString()
{
// Retrieve the cipher text from the registry; the process account must be
// granted Read access by the key's ACL
string encryptedString = (string)Registry.LocalMachine.OpenSubKey(
@"Software\OrderProcessing\")
.GetValue("ConnectionString");
// Use the managed DPAPI helper library to decrypt the string
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. 在 Microsoft Visual C#_ using 语句中创建了 SqlConnection 对象。这可确保无论是否发生异常,连接都会在方法中关闭。这将降低拒绝服务攻击的威胁,这种攻击试图使用所有可用的数据库连接。您可以通过使用 finally 块得到类似的功能。
4. 使用参数化的存储过程进行数据访问。这是另一个防止 SQL 注入攻击的对策。
5. 不将详细的错误信息返回给客户端。对异常详细信息进行记录,以辅助问题的诊断。
6. 加密的数据库连接字符串存储在注册表中。最安全的存储数据库连接字符串的方式之一,是使用 DPAPI 加密字符串和将加密的密文存储在一个受到保护的带有受限 ACL 的注册表项下。(例如,使用管理员:Full Control 和 ASP.NET 或者企业服务进程帐户:Read,这取决于哪个进程承载着组件。)
注 代码说明了如何从注册表中检索连接字符串,然后使用托管的 DPAPI 辅助库将其解密。这个库是在“如何创建 DPAPI 库”中提供的,该文章在“Microsoft patterns & practices 第 I 卷,构建安全的 ASP.NET Web 应用程序:身份验证、授权和安全通讯”的“如何……”部分中。
通过设置访问某些方法的访问角色来控制代码的安全性。代码如下:
代码应该在用户连接数据库之前根据角色或者标识对其授权。角色检查通常用在应用程序的业务逻辑中,但是如果您没有明确地区分业务和数据访问逻辑,则应该在访问数据库的方法上使用主体权限要求。
1)以下属性确保了只有是 Manager 角色成员的用户可调用 DisplayCustomerInfo 方法:
[PrincipalPermissionAttribute(SecurityAction.Demand, Role="Manager")]
public void DisplayCustomerInfo(int CustId)
{
}
2)以下代码片段使用一个显式的、编程实现的角色检查确保调用方是 Manager 角色的成员:
public void DisplayCustomerInfo(int CustId)
{
if(!Thread.CurrentPrincipal.IsInRole("Manager"))
{
. . .
}
}
2、保护敏感数据,通过添加salt 值的密码散列值:
如果您需要实现包含用户名和密码的用户存储区,则不要以明文或者加密的格式存储密码。不要存储密码,而应该存储带附加 salt 的非可逆散列值以降低字典攻击的风险。
注 salt 值是一个强加密的随机数。
创建 salt 值
以下代码说明了如何通过使用 System.Security.Cryptography 命名空间中的 RNGCryptoServiceProvider 类提供的随机数生成功能生成 salt 值。
public static string CreateSalt(int size)
{
RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider();
byte[] buff = new byte[size];
rng.GetBytes(buff);
return Convert.ToBase64String(buff);
}
创建(带 salt 值的)散列值
以下代码片段说明了如何从提供的密码和 salt 值生成散列值。
public static string CreatePasswordHash(string pwd, string salt)
{
string saltAndPwd = string.Concat(pwd, salt);
string hashedPwd =
FormsAuthentication.HashPasswordForStoringInConfigFile(
saltAndPwd, "SHA1");
return hashedPwd;
}
3)捕获和记录 ADO.NET 异常
将数据访问代码置于 try / catch 块中并处理异常。在编写 ADO.NET 数据访问代码时,ADO.NET 所生成的异常类型取决于数据提供程序。例如: • SQL Server .NET Framework 数据提供程序将生成 SqlExceptions。
• OLE DB .NET Framework 数据提供程序将生成 OleDbExceptions。
• ODBC .NET Framework 数据提供程序将生成 OdbcExceptions。
捕获异常
以下代码使用 SQL Server .NET Framework 数据提供程序,并说明了如何捕获 SqlException 类型的异常。
try
{
// Data access code
}
catch (SqlException sqlex) // more specific
{
}
catch (Exception ex) // less specific
{
}
日志记录异常
您还应该将来自 SqlException 类的详细信息记录下来。这个类公开了包含异常情况详细信息的属性。这包括一个说明错误的 Message 属性,一个唯一标识错误类型的 Number 属性,和一个包含其他信息的 State 属性。State 属性通常用来指示特定错误情况的某次出现。例如,如果存储过程在不止一行中出现了同样的错误,State 属性可指示特定的那一次。最后,Errors 集合包含可提供详细 SQL Server 错误信息的 SqlError 对象。
以下代码片段说明了如何通过使用 SQL Server .NET Framework 数据提供程序处理 SQL Server 错误情况:
using System.Data;
using System.Data.SqlClient;
using System.Diagnostics;
// Method exposed by a Data Access Layer (DAL) Component
public string GetProductName( int ProductID )
{
SqlConnection conn = new SqlConnection(
"server=(local);Integrated Security=SSPI;database=products");
// Enclose all data access code within a try block
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();
// The finally code is executed before the method returns
return paramPN.Value.ToString();
}
catch (SqlException sqlex)
{
// Handle data access exception condition
// Log specific exception details
LogException(sqlex);
// Wrap the current exception in a more relevant
// outer exception and re-throw the new exception
throw new Exception(
"Failed to retrieve product details for product ID: " +
ProductID.ToString(), sqlex );
}
finally
{
conn.Close(); // Ensures connection is closed
}
}
// Helper routine that logs SqlException details to the
// Application event log
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 );
}
}
4)在 ASP.NET 应用程序中使用一般性错误页
如果您的数据访问代码是由一个 ASP.NET Web 应用程序或者 Web 服务调用的,应该配置 <customErrors> 元素以防止异常详细信息传回最终用户。您还可以通过使用这个元素指定一般性错误页,如下所示。
<customErrors mode="On" defaultRedirect="YourErrorPage.htm" />
为产品服务器设置 mode="On" 。当您在发布前开发和测试软件时,只能使用 mode="Off"。如果无法做到这一点,将导致返回给最终用户丰富的错误信息,如图 4 中所示。这些信息可能包括数据库服务器名称、数据库名称和连接凭据。
图 4. 详细的异常信息会暴露敏感的数据
图 4 还显示了在导致异常的行附近的数据访问代码中仍然存在的许多缺陷。具体如下: • 连接字符串是硬编码的。
• 使用了高特权 sa 帐户来连接数据库。
• sa 帐户的密码很脆弱。
• SQL 命令构造很容易遭到 SQL 注入攻击,输入没有进行验证,而代码没有使用参数化的存储过程
下面是一个综合的示例演示如何安全操作数据层:
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) Code protected by try/catch block
try
{
// (2) Input validated with regular expression
// Error messages should be retrieved from the resource assembly to help
// localization. The Localization code is omitted for the sake of brevity.
if (Regex.IsMatch(productCode, "^[A-Za-z0-9]{12}$") == false)
throw new ArgumentException("Invalid product code" );
//(3) The using statement ensures that the connection is closed
using (SqlConnection conn = new SqlConnection(GetConnectionString()))
{
// (4) Use of parameterized stored procedures is a countermeasure for
// SQL injection attacks
SqlCommand cmd = new SqlCommand("spCheckProduct", conn);
cmd.CommandType = CommandType.StoredProcedure;
// Parameters are type checked
SqlParameter parm =
cmd.Parameters.Add("@ProductCode",
SqlDbType.VarChar,12);
parm.Value = productCode;
// Define the output parameter
SqlParameter retparm = cmd.Parameters.Add("@quantity", SqlDbType.Int);
retparm.Direction = ParameterDirection.Output;
conn.Open();
cmd.ExecuteNonQuery();
quantity = (int)retparm.Value;
}
}
catch (SqlException sqlex)
{
// (5) Full exception details are logged. Generic (safe) error message
// is thrown back to the caller based on the SQL error code
// Log and error identification code has been omitted for clarity
throw new Exception("Error Processing Request");
}
catch (Exception ex)
{
// Log full exception details
throw new Exception("Error Processing Request");
}
return quantity;
}
// (6) Encrypted database connection string is held in the registry
private static string GetConnectionString()
{
// Retrieve the cipher text from the registry; the process account must be
// granted Read access by the key's ACL
string encryptedString = (string)Registry.LocalMachine.OpenSubKey(
@"Software\OrderProcessing\")
.GetValue("ConnectionString");
// Use the managed DPAPI helper library to decrypt the string
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. 在 Microsoft Visual C#_ using 语句中创建了 SqlConnection 对象。这可确保无论是否发生异常,连接都会在方法中关闭。这将降低拒绝服务攻击的威胁,这种攻击试图使用所有可用的数据库连接。您可以通过使用 finally 块得到类似的功能。
4. 使用参数化的存储过程进行数据访问。这是另一个防止 SQL 注入攻击的对策。
5. 不将详细的错误信息返回给客户端。对异常详细信息进行记录,以辅助问题的诊断。
6. 加密的数据库连接字符串存储在注册表中。最安全的存储数据库连接字符串的方式之一,是使用 DPAPI 加密字符串和将加密的密文存储在一个受到保护的带有受限 ACL 的注册表项下。(例如,使用管理员:Full Control 和 ASP.NET 或者企业服务进程帐户:Read,这取决于哪个进程承载着组件。)
注 代码说明了如何从注册表中检索连接字符串,然后使用托管的 DPAPI 辅助库将其解密。这个库是在“如何创建 DPAPI 库”中提供的,该文章在“Microsoft patterns & practices 第 I 卷,构建安全的 ASP.NET Web 应用程序:身份验证、授权和安全通讯”的“如何……”部分中。
相关文章推荐
- 一些常用类的总结(日期方面的,math方面的,比较器)
- mongodb使用方面的一些总结
- 关于工作流设计方面的一些经验总结
- YUI 的 datatable 在使用方面的一些问题的总结
- 4次应聘经历后的一些非技术方面的总结
- 测试方面的一些自己的总结
- 总结了一些MySQL优化方面的技巧
- 在Android中使用adb命令时关于权限方面的一些总结
- 本人服务器遭受黑客长期攻击,特把这几天做的一些有用的安全方面总结出来,以方便以后查阅
- 对近期“工作流”方面的一些总结
- 最近做项目的一些关于重构方面的总结
- 在Android中使用adb命令时关于权限方面的一些总结
- 【转】在Android中使用adb命令时关于权限方面的一些总结
- CSS布局方面的一些小总结
- 今天这篇文章给了一些推荐方面的思路 —— 要整理总结推荐方面的工作了
- 关于这两天看CBR方面的论文的一些总结
- 总结下 数据库方面 的一些知识(Oracle方面)
- 一直都想总结一下自己遇到过的web安全方面的问题--2015-03-31
- 总结一些更多的针对webkit的HTML, CSS和Javascript方面的特性.
- 关于密码方面的一些总结