基本 SQL 注入和缓解措施(附示例)

mysqldatabasephp

SQL 注入是一种网络攻击,允许攻击者在数据库上执行恶意 SQL 语句。这些语句可用于操纵数据、检索敏感信息,甚至删除整个数据库。它是最常见和最危险的 Web 漏洞类型之一,会影响使用 SQL 数据库的任何网站或 Web 应用程序。

在本文中,我们将介绍 SQL 注入的基础知识、其工作原理以及如何预防。我们还将提供一个基本 SQL 注入攻击的示例,并展示如何缓解它。

什么是 SQL 注入?

SQL,即结构化查询语言,是一种用于管理和操作存储在关系数据库中的数据的编程语言。它是与数据库交互的标准语言,全球数百万个网站和应用程序都在使用它。

SQL 注入是一种利用基于 SQL 的应用程序中漏洞的网络攻击。它允许攻击者将恶意代码插入应用程序的 SQL 语句中,然后数据库可以执行这些代码。这可以让攻击者未经授权访问敏感数据、修改或删除数据,甚至控制整个数据库。

SQL 注入如何工作?

SQL 注入攻击通过利用设计不良或实施不良的 SQL 代码来工作。当应用程序收到用户输入时,通常会直接将其合并到 SQL 查询中,而无需进行适当的验证或清理。这可以让攻击者将恶意代码插入查询中,然后数据库可以执行这些代码。

例如,考虑一个要求输入用户名和密码的简单登录表单。应用程序可能会生成类似这样的 SQL 查询来验证用户的凭据 &miuns;

SELECT * FROM users WHERE username='$username' AND password='$password';

在这种情况下,$username 和 $password 变量将替换为用户的输入。如果用户输入自己的用户名和密码,查询将按预期工作。但是,如果攻击者输入恶意输入,他们可以操纵查询来执行诸如检索敏感数据甚至删除整个表之类的操作。

例如,攻击者可能会输入以下内容作为密码 −

' OR 1=1; -
这将修改查询使其看起来像这样 -

SELECT * FROM users WHERE username='$username' AND password='' OR 1=1;
--';

OR 1=1 语句的计算结果始终为真,因此查询将返回用户表中的所有行。末尾的 -- 是 SQL 注释,它告诉数据库忽略其后的所有内容。这允许攻击者绕过查询的其余部分并获得对整个表的访问权限。

如何防止 SQL 注入?

防止 SQL 注入攻击需要结合良好的设计实践和适当的输入验证。以下是您可以采取的一些步骤来保护您的应用程序 -

  • 使用参数化查询 - 防止 SQL 注入的最简单、最有效的方法之一是使用参数化查询。这涉及将 SQL 代码与用户输入分开,并将输入作为单独的参数传递。这可确保输入被视为值,而不是 SQL 代码的一部分,并使攻击者更难注入恶意代码。

  • 验证和清理用户输入 - 另一个重要步骤是验证和清理所有用户输入。这涉及检查输入中是否存在任何可能表明试图注入恶意代码的字符或模式。您还应该限制用户可以输入的输入类型和长度。

  • 使用准备好的语句 - 准备好的语句是一种参数化查询,可用于防止 SQL 注入。它们允许您为 SQL 语句创建模板,然后在稍后传入参数。这有助于防止 SQL 注入,因为参数在传递给准备好的语句之前不会被解析,这意味着任何恶意代码都将被视为值,而不是 SQL 代码的一部分。

  • 使用存储过程 - 存储过程是存储在数据库中的预编译 SQL 语句。它们可用于执行复杂查询并执行各种任务,例如插入、更新或删除数据。由于存储过程是预先编译的,因此它们比常规 SQL 语句更快、更高效。它们还可以帮助防止 SQL 注入,因为输入作为单独的参数传递给存储过程,而不是直接合并到 SQL 代码中。

  • 强制使用强密码 - 攻击者获取数据库访问权限的最常见方法之一是猜测或破解弱密码。为防止这种情况,您应该强制使用强密码策略,包括使用难以猜测或破解的长密码。您还应考虑使用双因素身份验证或其他安全措施来保护敏感帐户。

示例:基本 SQL 注入攻击和缓解措施

为了说明 SQL 注入的基础知识,让我们来看一个容易受到注入攻击的简单登录表单示例。然后,我们将展示如何使用参数化查询来缓解漏洞。

首先,让我们在 MySQL 数据库中创建一个简单的表来保存我们的用户 −

CREATE TABLE users ( id INT AUTO_INCREMENT PRIMARY KEY, username VARCHAR(50) NOT NULL, password VARCHAR(50) NOT NULL );

接下来,让我们用一个简单的 HTML 表单创建一个登录表单 −

<form action="login.php" method="post"> <label for="username">Username:</label> <input type="text" name="username" id="username"> <br> <label for="password">Password:</label> <input type="password" name="password" id="password"> <br> <input type="submit" value="Log In"> </form>

示例

表单向 login.php 发送一个 POST 请求,其中包含用户名和密码字段。然后我们可以使用 PHP 来处理请求并根据数据库检查用户的凭据−

<?php // Connect to the database $db = new mysqli("localhost", "username", "password", "database"); // Check if the form has been submitted if (isset($_POST["username"]) && isset($_POST["password"])) { // Get the username and password from the form $username = $_POST["username"]; $password = $_POST["password"]; // Create the SQL query $query = "SELECT * FROM users WHERE username='$username' AND password='$password'"; // Execute the query $result = $db->query($query); // Check if the query returned any rows if ($result->num_rows > 0) { // The username and password are correct echo "Logged in successfully!"; } else { // The username and password are incorrect echo "Invalid username or password"; } } ?>

此代码使用表单中的"用户名"和"密码"字段创建 SQL 查询,然后使用"mysqli"对象的"query()"方法执行查询。如果查询返回任何行,则表示用户名和密码正确,并且用户已登录。

但是,此代码容易受到 SQL 注入攻击。攻击者可以将恶意输入输入到表单中,这些输入将直接合并到 SQL 查询中。例如,如果攻击者输入以下内容作为其用户名 −

admin' --

生成的 SQL 查询将如下所示 −

SELECT * FROM users WHERE username='admin' --' AND password='$password';

末尾的 `--` 是 SQL 注释,它告诉数据库忽略其后的所有内容。这样一来,即使攻击者不知道密码,他们也可以绕过查询的其余部分并获得 `admin` 用户的访问权限。

示例

为了缓解此漏洞,我们可以使用参数化查询,而不是直接从用户输入构建 SQL 查询。以下是使用参数化查询的代码 −

<?php // Connect to the database $db = new mysqli("localhost", "username", "password", "database"); // Check if the form has been submitted if (isset($_POST["username"]) && isset($_POST["password"])) { // Get the username and password from the form $username = $_POST["username"]; $password = $_POST["password"]; // Create a prepared statement $stmt = $db->prepare("SELECT * FROM users WHERE username=? AND password=?"); // Bind the parameters $stmt->bind_param("ss", $username, $password); // Execute the statement $stmt->execute(); // Get the result $result = $stmt->get_result(); // Check if the query returned any rows if ($result->num_rows > 0) { // The username and password are correct echo "Logged in successfully!"; } else { // The username and password are incorrect echo "Invalid username or password"; } } ?>

在此版本的代码中,我们使用准备好的语句来创建 SQL 查询的模板。然后,我们使用 bind_param() 方法将用户名和密码变量作为参数绑定到准备好的语句。这可确保将输入视为值,而不是 SQL 代码的一部分,从而使攻击者更难注入恶意代码。

结论

SQL 注入是一种严重且普遍的安全漏洞,可能会损害数据库的完整性和机密性。为了保护您的应用程序和数据,遵循设计和实施 SQL 代码的最佳实践并使用适当的输入验证和清理技术非常重要。通过使用参数化查询和其他预防措施,您可以帮助防止 SQL 注入攻击并确保应用程序和数据的安全。


相关文章