如何在 PHP 应用程序中预防SQL 注入

SQL 注入是本书中的一个古老技巧,以至于每个人,每个开发人员都非常了解它,并且非常清楚如何防止它。除了他们滑倒的那一次奇怪的时候,结果简直是灾难性的。

如果您已经知道什么是 SQL 注入,请随时跳到文章的后半部分。但是对于那些刚刚进入 Web 开发领域并梦想担任更高级职位的人来说,一些介绍是必要的。

什么是 SQL 注入?

理解 SQL Injection 的关键在于它的名字:SQL + Injection。这里的“注射”一词没有任何医学内涵,而是动词“注射”的用法。这两个词一起传达了将 SQL 放入 Web 应用程序的想法。

将 SQL 放入 Web 应用程序。. . 嗯。. . 这不就是我们正在做的吗?是的,但我们不希望攻击者驱动我们的数据库。让我们在一个例子的帮助下理解这一点。

假设您正在为本地电子商务商店构建一个典型的 PHP 网站,因此您决定添加这样的联系表单:

<form action="record_message.php" method="POST">
  <label>Your name</label>
  <input type="text" name="name">
  
  <label>Your message</label>
  <textarea name="message" rows="5"></textarea>
  
  <input type="submit" value="Send">
</form>
复制

让我们假设该文件send_message.php将所有内容存储在数据库中,以便商店所有者稍后可以读取用户消息。它可能有一些这样的代码:

<?php

$name = $_POST['name'];
$message = $_POST['message'];

// check if this user already has a message
mysqli_query($conn, "SELECT * from messages where name = $name");

// Other code here
复制

因此,您首先尝试查看该用户是否已有未读消息。查询SELECT * from messages where name = $name似乎很简单,对吧?

错误的!

出于天真,我们打开了立即销毁数据库的大门。为此,攻击者必须满足以下条件:

  • 该应用程序在 SQL 数据库上运行(今天,几乎每个应用程序都是)
  • 当前数据库连接对数据库具有“编辑”和“删除”权限
  • 可以猜到重要表的名字

第三点意味着,既然攻击者知道您正在经营一家电子商务商店,那么您很可能会将订单数据存储在一个orders表中。有了这一切,攻击者需要做的就是提供这个作为他们的名字:

Joe; truncate orders;? 是的先生!让我们看看查询被 PHP 脚本执行后会变成什么:

SELECT * FROM messages WHERE name = Joe; truncate orders;

好的,查询的第一部分有一个语法错误(“Joe”周围没有引号),但是分号强制 MySQL 引擎开始解释一个新的:truncate orders. 就这样,一口气,整个订单历史都消失了!

现在您已经了解了 SQL 注入的工作原理,是时候看看如何阻止它了。SQL注入成功需要满足的两个条件是:

  1. PHP 脚本应该对数据库具有修改/删除权限。我认为这适用于所有应用程序,您将无法将您的应用程序设为只读。🙂 你猜怎么着,即使我们删除所有修改权限,SQL 注入仍然可以允许某人运行 SELECT 查询并查看所有数据库,包括敏感数据。换句话说,降低数据库访问级别是行不通的,您的应用程序无论如何都需要它。
  2. 正在处理用户输入。SQL 注入起作用的唯一方法是当您接受来自用户的数据时。再次强调,仅仅因为担心 SQL 注入就停止应用程序的所有输入是不切实际的。

防止 PHP 中的 SQL 注入

现在,鉴于数据库连接、查询和用户输入是生活的一部分,我们如何防止 SQL 注入?值得庆幸的是,它非常简单,有两种方法可以做到:1)清理用户输入,2)使用准备好的语句。

清理用户输入

如果您使用的是较旧的 PHP 版本(5.5 或更低版本,并且在共享主机上经常发生这种情况),明智的做法是通过一个名为mysql_real_escape_string(). 基本上,它的作用是删除字符串中的所有特殊字符,以便它们在数据库使用时失去意义。

例如,如果您有一个类似 的字符串I'm a string,攻击者可以使用单引号字符 (') 来操纵正在创建的数据库查询并导致 SQL 注入。通过 products 运行它,这mysql_real_escape_string()I\'m a string在单引号中添加一个反斜杠,将其转义。结果,整个字符串现在作为无害的字符串传递给数据库,而不是能够参与查询操作。

这种方法有一个缺点:它是一种非常非常古老的技术,与 PHP 中较旧的数据库访问形式一起使用。从 PHP 7 开始,这个函数甚至不再存在,这为我们带来了下一个解决方案。

使用准备好的语句

准备好的语句是一种使数据库查询更安全可靠的方法。这个想法是,我们首先告诉数据库我们将要发送的查询的结构,而不是将原始查询发送到数据库。这就是我们所说的“准备”声明。准备好语句后,我们将信息作为参数化输入传递,以便数据库可以通过将输入插入我们之前发送的查询结构来“填补空白”。这会带走输入可能具有的任何特殊功能,导致它们在整个过程中仅被视为变量(或有效负载,如果您愿意的话)。这是准备好的语句的样子:

<?php
$servername = "localhost";
$username = "username";
$password = "password";
$dbname = "myDB";

// Create connection
$conn = new mysqli($servername, $username, $password, $dbname);

// Check connection
if ($conn->connect_error) {
    die("Connection failed: " . $conn->connect_error);
}

// prepare and bind
$stmt = $conn->prepare("INSERT INTO MyGuests (firstname, lastname, email) VALUES (?, ?, ?)");
$stmt->bind_param("sss", $firstname, $lastname, $email);

// set parameters and execute
$firstname = "John";
$lastname = "Doe";
$email = "john@example.com";
$stmt->execute();

$firstname = "Mary";
$lastname = "Moe";
$email = "mary@example.com";
$stmt->execute();

$firstname = "Julie";
$lastname = "Dooley";
$email = "julie@example.com";
$stmt->execute();

echo "New records created successfully";

$stmt->close();
$conn->close();
?>
复制

我知道如果您不熟悉准备好的陈述,这个过程听起来不必要地复杂,但这个概念非常值得付出努力。这是一个很好的介绍。

对于那些已经熟悉 PHP 的 PDO 扩展并使用它来创建准备好的语句的人,我有一个小小的建议。

警告:设置 PDO 时要小心

当使用 PDO 访问数据库时,我们可能会陷入一种错误的安全感。“啊,好吧,我正在使用 PDO。现在我不需要考虑其他任何事情”——这就是我们通常的思维方式。确实,PDO(或 MySQLi 准备好的语句)足以防止各种 SQL 注入攻击,但在设置时必须小心。通常只从教程或早期项目中复制粘贴代码并继续,但此设置可以撤消所有操作:

$dbConnection->setAttribute(PDO::ATTR_EMULATE_PREPARES, true);
复制

此设置的作用是告诉 PDO 模拟预准备语句,而不是实际使用数据库的预准备语句功能。因此,PHP 会向数据库发送简单的查询字符串,即使您的代码看起来像是在创建准备好的语句和设置参数等等。换句话说,您和以前一样容易受到 SQL 注入的影响。🙂

解决方案很简单:确保此仿真设置为 false。

$dbConnection->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
复制

现在 PHP 脚本被迫在数据库级别使用准备好的语句,防止各种 SQL 注入。

防止使用 WAF

您知道您还可以使用 WAF(Web 应用程序防火墙)保护 Web 应用程序免受 SQL 注入吗?

好吧,不仅仅是 SQL 注入,还有许多其他第 7 层漏洞,例如跨站点脚本、破坏身份验证、跨站点伪造、数据暴露等。您可以使用像Mod Security这样的自托管或基于云的以下漏洞。

SQL 注入和现代 PHP 框架

SQL 注入是如此常见、如此简单、如此令人沮丧和如此危险,以至于所有现代PHP Web 框架都内置了对策。例如,在 WordPress 中,我们有这个$wpdb->prepare()功能,而如果您使用的是 MVC 框架,它会为您完成所有繁琐的工作,您甚至不必考虑防止 SQL 注入。在 WordPress 中您必须明确地准备语句,这有点烦人,但是,嘿,我们正在谈论的是 WordPress。🙂

无论如何,我的观点是,现代网络开发人员不必考虑 SQL 注入,因此,他们甚至没有意识到这种可能性。因此,即使他们在应用程序中打开了一个后门(可能是 $_GET 查询参数和启动脏查询的旧习惯),结果也可能是灾难性的。因此,花时间深入了解基础总是更好。

结论

SQL 注入是对 Web 应用程序的一种非常恶劣的攻击,但很容易避免。正如我们在本文中看到的,在处理用户输入(顺便说一下,SQL 注入并不是处理用户输入带来的唯一威胁)和查询数据库时要小心。也就是说,我们并不总是致力于 Web 框架的安全性,因此最好注意这种类型的攻击,不要上当。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值