如何修复SQL注入漏洞?
2021-02-26

解决SQL注入缺陷最终要求基于代码的修复。防护建议包括部署分层安全措施(包括在接受用户输入时使用参数化的查询)、确保应用程序仅使用预期的数据、加固数据库服务器防止不恰当的访问数据。

建议使用以下措施防范SQL注入漏洞:

对于开发
========

使用以下建议编写不受SQL注入攻击影响的web应用。

参数化查询:SQL注入源于攻击者控制查询数据以修改查询逻辑,因此防范SQL注入攻击的最佳方式就是将查询的逻辑与其数据分隔,这可以防止执行从用户输入所注入的命令。这种方式的缺陷是可能对性能产生影响(但影响很小),且必须以这种方式构建站点上的每个查询才能完全有效。只要无意中绕过了一个查询,就足以导致应用受SQL注入的影响。以下代码显示的是可以进行SQL注入的SQL语句示例。

sSql = "SELECT LocationName FROM Locations "; sSql = sSql + " WHERE LocationID = " + Request["LocationID"]; oCmd.CommandText = sSql;

下面的例子使用了参数化的查询,不受SQL注入攻击的影响。

sSql = "SELECT * FROM Locations ";
sSql = sSql + " WHERE LocationID = @LocationID"; oCmd.CommandText = sSql; oCmd.Parameters.Add("@LocationID", Request["LocationID"]);

应用程序没有包含用户输入向服务器发送SQL语句,而是使用-@LocationID-参数替代该输入,这样用户输入就无法成为SQL执行的命令。这种方式可以有效的拒绝攻击者所注入的任何输入,尽管仍会生成错误,但仅为数据类型转换错误,而不是黑客可以利用的错误。

以下代码示例显示从HTTP查询字符串中获得产品ID并使用到SQL查询中。请注意传送给SqlCommand的包含有SELECT的字符串仅仅是个静态字符串,不是从输入中截取的。此外还请注意使用SqlParameter对象传送输入参数的方式,该对象的名称(@pid)匹配SQL查询中所使用的名称。

C#示例:

string connString = WebConfigurationManager.ConnectionStrings["myConn"].ConnectionString;
using (SqlConnection conn = new SqlConnection(connString))
{
conn.Open();
SqlCommand cmd = new SqlCommand("SELECT Count(*) FROM Products WHERE ProdID=@pid", conn);
SqlParameter prm = new SqlParameter("@pid", SqlDbType.VarChar, 50);
prm.Value = Request.QueryString["pid"];
cmd.Parameters.Add(prm);
int recCount = (int)cmd.ExecuteScalar();
}

VB.NET示例:

Dim connString As String = WebConfigurationManager.ConnectionStrings("myConn").ConnectionString

Using conn As New SqlConnection(connString) conn.Open()
Dim cmd As SqlCommand = New SqlCommand("SELECT Count(*) FROM Products WHERE ProdID=@pid", conn)
Dim prm As SqlParameter = New SqlParameter("@pid", SqlDbType.VarChar, 50)
prm.Value = Request.QueryString("pid")
cmd.Parameters.Add(prm)
Dim recCount As Integer = cmd.ExecuteScalar()
End Using

验证输入:可通过正确验证用户输入的类型和格式防范大多数SQL注入攻击,最佳方式是通过白名单,定义方法为对于相关的字段只接受特定的帐号号码或帐号类型,或对于其他仅接受英文字母表的整数或字母。很多开发人员都试图使用黑名单字符或转义的方式验证输入。总体上讲,这种方式通过在恶意数据前添加转义字符来拒绝已知的恶意数据,如单引号,这样之后的项就可以用作文字值。这种方式没有白名单有效,因为不可能事先知道所有形式的恶意数据。

对于安全操作
============

使用以下建议帮助防范对web应用的SQL注入攻击。

限制应用程序权限:限制用户凭据,仅使用应用运行所必需权限的。任何成功的SQL注入攻击都会运行在用户凭据的环境中,尽管限制权限无法完全防范SQL注入攻击,但可以大大增加其难度。

加强系统管理员口令策略:通常攻击者需要管理员帐号的功能才能使用特定的SQL命令,如果系统管理员口令较弱的话就比较容易暴力猜测,增加成功SQL注入攻击的可能性。另一个选项就是根本不使用系统管理员口令,而是为特定目的创建特定的帐号。

一致的错误消息方案:确保在出现数据库错误时向用户提供尽可能少的信息。不要泄漏整个错误消息,要同时在web和应用服务器上处理错误消息。当web服务器遇到处理错误时,应使用通用的web页面响应,或将用户重新定向到标准的位置。绝不要泄漏调试信息或其他可能对攻击者有用的细节。

有关如何在IIS中关闭详细错误消息的说明请见:
http://www.microsoft.com/windows2000/en/server/iis/default.asp?url= /windows2000/en/server/iis/htm/core/iierrcst.htm

使用以下句法在Apache服务器上取缔错误消息:

Syntax: ErrorDocument <3-digit-code>
Example: ErrorDocument 500 /webserver_errors/server_error500.txt

WebSphere之类的应用服务器通常默认安装启用了错误消息或调试设置。有关如何取缔这些错误消息的信息,请参考应用服务器文档。

存储过程:如果不使用的话,请删除master..Xp_cmdshell、xp_startmail、xp_sendmail、
sp_makewebtask之类的SQL存储过程。

SQL注入漏洞根本上还是取决于web应用程序的代码。尽管不是修复,但可以通过向IDS中添加结合了正则表达式的规则作为紧急措施检测SQL注入攻击。尽管这无法修复所有可能的SQL注入漏洞,但便于实施,并且要求攻击者必须要改进其方法才能实现成功的攻击。可如下使用正则表达式。

删除SQL元字符的正则表达式:
/(\%27)|(\')|(\-\-)|(\%23)|(#)/ix

可如下将上述正则表达式添加到Snort规则:
alert tcp $EXTERNAL_NET any -> $HTTP_SERVERS $HTTP_PORTS (msg:"SQL Injection- Paranoid";flow:to_server,established;uricontent:".pl";pcre:"/(\%27)|(\')|(\-\-)|(%23)|(#)/i"; classtype:Web-application-attack; sid:9099; rev:5;)

传统SQL注入攻击的正则表达式:
/\w*((\%27)|(\'))((\%6F)|o|(\%4F))((\%72)|r|(\%52))/ix

删除有UNION关键字的SQL注入攻击的正则表达式:
/((\%27)|(\'))union/ix
(\%27)|(\')

可为其他的SQL查询(如select、insert、update、delete、drop等)编写类似的正则表达式。

在MS SQL服务器上检测SQL注入攻击的正则表达式:
/exec(\s|\+)+(s|x)p\w+/ix