SQL 注入

本章节讲解什么是 SQL 注入,一般 SQL 注入的方法,以及如何防止被 SQL 注入。


什么是 SQL 注入

SQL 注入是一种可能破坏数据库的代码注入技术。

SQL 注入是最常见的 web 黑客技术之一。

SQL 注入是通过网页输入在 SQL 语句中放置恶意代码。


在网页中的 SQL

SQL 注入通常发生在您向用户请求输入时,比如 username/userid,而不是 name/id,用户会给您一条 SQL 语句,您将在 不知不觉 中在数据库上运行该语句。

请看下面的示例,该示例通过向 SELECT 字符串添加变量(txtUserId)来创建一个SELECT 语句。变量是从用户输入(getRequestString)获取的:

实例
  1. txtUserId = getRequestString("UserId");
  2. txtSQL = "SELECT *FROM Users WHERE UserId = " + txtUserId;

本章其余部分将介绍在 SQL 语句中使用用户输入的潜在危险。


基于 1=1 总是 True 的 SQL 注入

再看看上面的例子。代码的最初目的是创建一个 SQL 语句来选择具有给定用户 id 的用户。

如果没有任何东西阻止用户在输入框输入"wrong" 时,用户可以输入这样的内容:

UserId:

然后,SQL语句将如下所示:

  1. SELECT * FROM Users WHERE UserId = 105 OR 1=1;

上面的 SQL 是有效的,将从 "Users" 表中返回所有数据,因为 1=1 始终为 True

上面的例子看起来危险吗?如果 "Users" 表包含名称和密码?

上面的 SQL 语句与此基本相同:

  1. SELECT UserId, Name, PasswordFROM Users WHERE UserId = 105 or 1=1;

黑客只需在输入字段中插入 105 OR 1=1 ,就可以访问数据库中的所有用户名和密码。


基于 ""="" 总是 True 的 SQL 注入

Here is an example of a user login on a web site:

Username:

Password:

实例
  1. uName = getRequestString("username");
  2. uPass = getRequestString("userpassword");
  3. sql = 'SELECT * FROM Users WHERE Name ="' + uName + '" AND Pass ="' + uPass + '"'
结果
  1. SELECT * FROM Users WHERE Name ="John Doe" AND Pass ="myPass"

黑客只需将 " OR ""=" 插入用户名或密码文本框,就可以访问数据库用户名和密码:

User Name:

Password:

服务器上的代码将创建如下有效的 SQL 语句:

结果
  1. SELECT * FROM Users WHERE Name ="" or ""="" AND Pass ="" or ""=""

上面的 SQL 是有效的,因为 OR ""="" 总是 True,所以会返回表中所有数据行。


基于批处理 SQL 语句的 SQL 注入

大多数数据库支持批处理 SQL 语句。

一批 SQL 语句是由两个或多个 SQL 语句组成的一组,用分号分隔。

下面的 SQL 语句将返回 "Users" 表的所有数据,然后删除 "Suppliers" 表

实例
  1. SELECT * FROM Users; DROP TABLE Suppliers

看一看一下实例

实例
  1. txtUserId = getRequestString("UserId");
  2. txtSQL = "SELECT *FROM Users WHERE UserId = " + txtUserId;

然后如下输入:

User id:

有效的 SQL 语句如下所示:

Result
  1. SELECT * FROM Users WHERE UserId = 105; DROP TABLE Suppliers;

使用 SQL 参数化防止注入

要保护网站不受 SQL 注入的影响,可以将 SQL 参数化。

SQL 参数是在执行时以可控方式添加到 SQL 查询的值。

ASP.NET 标记实例
  1. txtUserId = getRequestString("UserId");
  2. txtSQL = "SELECT *FROM Users WHERE UserId = @0";
  3. db.Execute(txtSQL,txtUserId);

请注意,SQL 语句中的参数由 @ 标记表示。

SQL 引擎检查每个参数,以确保其列正确,并按字面意思处理,而不是作为要执行的 SQL 的一部分。

另一个实例
  1. txtNam = getRequestString("CustomerName");
  2. txtAdd = getRequestString("Address");
  3. txtCit = getRequestString("City");
  4. txtSQL = "INSERT INTO Customers (CustomerName,Address,City) Values(@0,@1,@2)";
  5. db.Execute(txtSQL,txtNam,txtAdd,txtCit);

实例

下面的示例演示如何用一些常见的 web 语言构建参数化查询。

ASP.NET 中的 SELECT 语句:

  1. txtUserId = getRequestString("UserId");
  2. sql = "SELECT * FROM Customers WHERE CustomerId = @0";
  3. command = new SqlCommand(sql);
  4. command.Parameters.AddWithValue("@0",txtUserId);
  5. command.ExecuteReader();

ASP.NET 中的 INSERT INTO 语句:

  1. txtNam = getRequestString("CustomerName");
  2. txtAdd = getRequestString("Address");
  3. txtCit = getRequestString("City");
  4. txtSQL = "INSERT INTO Customers (CustomerName,Address,City) Values(@0,@1,@2)";
  5. command = new SqlCommand(txtSQL);
  6. command.Parameters.AddWithValue("@0",txtNam);
  7. command.Parameters.AddWithValue("@1",txtAdd);
  8. command.Parameters.AddWithValue("@2",txtCit);
  9. command.ExecuteNonQuery();

PHP 中的 INSERT INTO 语句:

  1. $stmt = $dbh->prepare("INSERT INTO Customers (CustomerName,Address,City)
  2. VALUES (:nam, :add, :cit)");
  3. $stmt->bindParam(':nam', $txtNam);
  4. $stmt->bindParam(':add', $txtAdd);
  5. $stmt->bindParam(':cit', $txtCit);
  6. $stmt->execute();