跳到主要内容

SQL注入

提示
  1. SQL 注入的定义:SQL 注入是一种技术,可以通过表单输入或 URL 查询参数执行恶意 SQL 命令,导致未经授权的数据库访问。
  2. 注入方式:SQL 注入可能通过多种方式进行,包括使用多个语句(例如,通过表单输入添加 DROP TABLE 命令)或始终为真的条件(例如,将用户名和密码设为 " OR "1"="1")。
  3. 防御措施:预防 SQL 注入的方法包括验证用户输入、使用对象关系映射(ORM)工具、采用预处理语句以及使用安全框架(如 Django、Laravel、ASP.net 等)。

SQL 注入是一种技术,通过表单输入字段或 URL 查询参数执行 SQL 命令。这导致未经授权的数据库访问(一种黑客行为)。

如果 SQL 注入成功,未经授权的人可能会读取、创建、更新甚至删除数据库表中的记录。这种技术主要被(但不限于)黑客、渗透测试人员、QA 和安全研究员使用。

使用多个语句的 SQL 注入

假设我们的网站有一个搜索表单,用于按 ID 搜索产品。搜索产品的 PHP 代码片段可能看起来像这样,

$prod_id = $_GET["prod_id"];

$sql = "SELECT * FROM Products WHERE product_id = " . $prod_id;

如果用户在表单中输入 20; DROP TABLE Products;,那么 SQL 语句变成,

SELECT * FROM Products WHERE product_id = 20; DROP TABLE Products;

现在,这条 SQL 语句从数据库中删除了 Products 表。这是可能的,因为大多数数据库系统可以同时执行多个语句。

如何在应用中注入 SQL?

使用始终为真的条件进行 SQL 注入

另一种执行 SQL 注入的方法是传递始终导致 TRUE 的条件,这样无论如何都会获取数据。

让我们看看另一个 PHP 代码片段,我们的网站上有一个登录表单,我们需要提供凭据来获取用户信息。

$username = $_POST["username"];
$password = $_POST["password"];

$sql = "SELECT * FROM Users WHERE username = \"" . $username . "\" AND password = \"" . $password . "\"";

如果用户将用户名输入为 invalid_user" OR "1"="1 和密码输入为 invalid_pass" OR "1"="1,那么 SQL 语句变成

SELECT * FROM Users WHERE username = "invalid_user" OR "1"="1" AND password = "invalid_pass" OR "1"="1"

由于 "1"="1" 始终为 TRUE,无论用户输入什么 usernamepassword,SQL 都会从数据库中获取所有用户的数据。

如何保护 SQL 语句免受注入?

验证用户输入

我们应该在实际发送到数据库之前始终验证用户的输入数据。一些最佳实践包括:修剪空格,解析特殊字符,限制输入大小等。

例如,

$data = $_POST["name"];

$data = trim($data);
$data = stripslashes($data);
$data = htmlspecialchars($data);

这里,这段 PHP 代码片段在某种程度上验证了输入数据。

使用 ORM

ORM(对象关系映射)是一种工具,基本上将 SQL 语句解析为编程语言代码,反之亦然。

如果我们使用 ORM,我们大部分情况下都不必编写原始 SQL。由于 ORM 是按照良好实践和安全协议设计的,它们既安全又易于使用。

例如,考虑下面的 SQL 代码:

SELECT * FROM Users WHERE id = 5;

它在 Python 的 SQLAlchemy ORM 中的等效代码将是:

select(Users).where(Users.id == 5)

使用预处理语句

另一种保护 SQL 语句免受注入的方法是使用预处理语句。

预处理语句基本上是带有占位符的 SQL 语句。传递的参数只是替换占位符的位置。

例如,

$sql = "INSERT INTO Users (first_name, last_name, email) VALUES (?, ?, ?)";

mysqli_stmt_bind_param($sql, "sss", $first_name, $last_name, $email);

$first_name = "Harry";
$last_name = "Potter";
$email = "harrypotter@mail.com";

mysqli_stmt_execute($stmt);

这里,值

只是放在 ? 占位符的位置,SQL 语句的结构被保留。

使用框架

此外,如果我们正在构建一个实际应用,使用框架(如 Django、Laravel、ASP.net 等)而不是从头开始编写代码始终是一个好主意。

这是因为这些框架默认处理 SQL 注入和许多其他常见问题。

结论

SQL 注入是黑客攻击任何网络应用的一种非常常见的方法。

因此,如果我们在构建应用时使用原始 SQL 语句,我们必须彻底测试和验证它们。