SQL注入如何预防?
本文参考自owasp,重点是提供清晰,简单,可操作的指导,以防止应用程序中的SQL注入漏洞。不幸的是,SQL注入攻击很常见,这是由于两个因素:
- SQL注入漏洞的显着流行
- 目标的吸引力(即数据库通常包含应用程序的所有有趣/关键数据)。
发生了如此多的成功SQL注入攻击有点可耻,因为在代码中避免SQL注入漏洞非常简单。
当软件开发人员创建包含用户提供的输入的动态数据库查询时,会引入SQL注入漏洞。为了避免SQL注入缺陷很简单。开发人员需要:
a)停止编写动态查询;
b)防止用户提供的包含恶意SQL的输入影响所执行查询的逻辑。
本文提供了一组通过避免这两个问题来防止SQL注入漏洞的简单技术。这些技术几乎可以与任何类型的数据库一起使用。还有其他类型的数据库,如XML数据库,可能有类似的问题(例如,XPath和XQuery注入),这些技术也可用于保护它们。
主要防御:
- 选项1:使用准备好的语句(带参数化查询)
- 选项2:使用存储过程
- 选项3:白名单输入验证
- 选项4:转义所有用户提供的输入
额外防御:
- 另外:强制执行最低权限
- 另外:执行白名单输入验证作为辅助防御
不安全的例子:
SQL注入漏洞通常如下所示:
以下(Java)示例是UNSAFE,并允许攻击者将代码注入将由数据库执行的查询中。简单地附加到查询的未经验证的“customerName”参数允许攻击者注入他们想要的任何SQL代码。不幸的是,这种访问数据库的方法太常见了。
String query = "SELECT account_balance FROM user_data WHERE user_name = "
+ request.getParameter("customerName");
try {
Statement statement = connection.createStatement( ... );
ResultSet results = statement.executeQuery( query );
}
...
主要防御
防御选项1:准备好的语句(带参数化查询)#
使用带有变量绑定的预准备语句(也就是参数化查询)是所有开发人员应该首先学习如何编写数据库查询的方法。它们比动态查询更易于编写,更易于理解。参数化查询强制开发人员首先定义所有SQL代码,然后将每个参数传递给查询。这种编码风格允许数据库区分代码和数据,无论提供什么用户输入。
准备好的语句可确保攻击者无法更改查询的意图,即使攻击者插入了SQL命令也是如此。在下面的安全示例中,如果攻击者输入的是userID tom' or '1'='1
,则参数化查询不会受到攻击,而是会查找与字符串完全匹配的用户名tom' or '1'='1
。
特定语言的建议:
- Java EE -
PreparedStatement()
与绑定变量一起使用 - .NET - 使用参数化查询,如绑定变量
SqlCommand()
或OleDbCommand()
使用绑定变量 - PHP - 将PDO与强类型参数化查询一起使用(使用bindParam())
- Hibernate -
createQuery()
与绑定变量一起使用(在Hibernate中称为命名参数) - SQLite - 用于
sqlite3_prepare()
创建语句对象
在极少数情况下,准备好的陈述会损害绩效。遇到这种情况时,最好是a)强烈验证所有数据或b)使用特定于数据库供应商的转义例程来转义所有用户提供的输入,如下所述,而不是使用预准备语句。
安全JavaSQL语句示例
以下代码示例使用PreparedStatement
Java的参数化查询实现来执行相同的数据库查询。
// 一定要验证
String custname = request.getParameter("customerName");
String query = "SELECT account_balance FROM user_data WHERE user_name = ? ";
PreparedStatement pstmt = connection.prepareStatement( query );
pstmt.setString( 1, custname);
ResultSet results = pstmt.executeQuery( );
安全C#.NET SQL语句示例
使用.NET,它更加直接。查询的创建和执行不会更改。您所要做的就是使用Parameters.Add()
此处所示的调用将参数传递给查询。
String query = "SELECT account_balance FROM user_data WHERE user_name = ?";
try {
OleDbCommand command = new OleDbCommand(query, connection);
command.Parameters.Add(new OleDbParameter("customerName", CustomerName Name.Text));
OleDbDataReader reader = command.ExecuteReader();
// …
} catch (OleDbException se) {
// error handling
}