最好的方法必须是参数化查询。这样,用户输入的内容无关紧要,数据作为值传递到数据库。
网上快速搜索显示PHP中的一些可能性,这很棒!甚至在这个网站上 - https://php.net/manual/en/pdo.prepared-statements.php
这也说明了为什么这对于安全性和性能都很好。
SQL 注入是一种攻击技术,攻击者利用构建动态 SQL 查询的应用程序代码中的漏洞。攻击者可以访问应用程序的权限部分,检索数据库中的所有信息,篡改现有数据,甚至在数据库主机上执行危险的系统级命令。当开发人员在其 SQL 语句中连接或插入任意输入时,就会出现此漏洞。
示例 #1 将结果集拆分为页面……并创建超级用户 (PostgreSQL)
在以下示例中,用户输入直接插入到 SQL 查询中,允许攻击者在数据库中获得超级用户帐户。
<?php
$offset = $_GET['offset']; // 注意,没有输入验证!
$query = "SELECT id, name FROM products ORDER BY name LIMIT 20 OFFSET $offset;";
$result = pg_query($conn, $query);
?>
0; insert into pg_shadow(usename,usesysid,usesuper,usecatupd,passwd) select 'crack', usesysid, 't','t','crack' from pg_shadow where usename='postgres'; --
0;
用于为原始查询提供有效的偏移量并终止它。
注意:
使用
--
(SQL 中的注释符号)强制 SQL 解析器忽略开发人员编写的查询其余部分是一种常见技术。
绕过搜索结果页面来获取密码是一种可行的方法。攻击者唯一需要做的是查看 SQL 语句中是否使用了任何未正确处理的提交变量。这些过滤器通常可以在前面的表单中设置,以自定义 SELECT
语句中的 WHERE、ORDER BY、LIMIT
和 OFFSET
子句。如果您的数据库支持 UNION
结构,则攻击者可能会尝试将整个查询附加到原始查询中,以列出任意表中的密码。强烈建议仅存储密码的安全哈希值,而不是密码本身。
示例 #2 列出文章……以及一些密码(任何数据库服务器)
<?php
$query = "SELECT id, name, inserted, size FROM products
WHERE size = '$size'";
$result = odbc_exec($conn, $query);
?>
SELECT
语句组合,该语句显示所有密码' union select '1', concat(uname||'-'||passwd) as name, '1971-01-01', '0' from usertable; --
UPDATE
和 INSERT
语句也容易受到此类攻击。
示例 #3 从重置密码……到获得更多权限(任何数据库服务器)
<?php
$query = "UPDATE usertable SET pwd='$pwd' WHERE uid='$uid';";
?>
' or uid like'%admin%
到 $uid 以更改管理员的密码,或者简单地将 $pwd 设置为 hehehe', trusted=100, admin='yes
以获得更多权限,则查询将被扭曲<?php
// $uid: ' or uid like '%admin%
$query = "UPDATE usertable SET pwd='...' WHERE uid='' or uid like '%admin%';";
// $pwd: hehehe', trusted=100, admin='yes
$query = "UPDATE usertable SET pwd='hehehe', trusted=100, admin='yes' WHERE
...;";
?>
虽然攻击者必须至少掌握一些数据库架构知识才能成功进行攻击,但这信息通常很容易获得。例如,代码可能是开源软件的一部分,并且公开可用。此信息也可能由闭源代码泄露——即使它被编码、混淆或编译——甚至可能由您自己的代码通过显示错误消息泄露。其他方法包括使用典型的表和列名称。例如,使用名为“users”的表以及列名“id”、“username”和“password”的登录表单。
示例 #4 攻击数据库主机操作系统 (MSSQL Server)
一个可怕的例子,说明如何在某些数据库主机上访问操作系统级命令。
<?php
$query = "SELECT * FROM products WHERE id LIKE '%$prod%'";
$result = mssql_query($query);
?>
a%' exec master..xp_cmdshell 'net user test testpass /ADD' --
到 $prod,则 $query 将为<?php
$query = "SELECT * FROM products
WHERE id LIKE '%a%'
exec master..xp_cmdshell 'net user test testpass /ADD' --%'";
$result = mssql_query($query);
?>
sa
运行,并且 MSSQLSERVER 服务以足够的权限运行,则攻击者现在将拥有一个帐户来访问此机器。
注意:
上面的一些示例与特定的数据库服务器相关联,但这并不意味着对其他产品的类似攻击是不可能的。您的数据库服务器可能以其他方式同样容易受到攻击。
避免 SQL 注入的推荐方法是通过预处理语句绑定所有数据。使用参数化查询不足以完全避免 SQL 注入,但它是向 SQL 语句提供输入的最简单和最安全的方法。 WHERE
、SET
和 VALUES
子句中的所有动态数据文本都必须替换为占位符。实际数据将在执行期间绑定,并与 SQL 命令分开发送。
参数绑定只能用于数据。SQL 查询的其他动态部分必须针对已知的允许值列表进行过滤。
示例 #5 使用 PDO 预处理语句避免 SQL 注入
<?php
// 动态SQL部分已针对预期值进行验证
$sortingOrder = $_GET['sortingOrder'] === 'DESC' ? 'DESC' : 'ASC';
$productId = $_GET['productId'];
// SQL 使用占位符进行准备
$stmt = $pdo->prepare("SELECT * FROM products WHERE id LIKE ? ORDER BY price {$sortingOrder}");
// 使用 LIKE 通配符提供值
$stmt->execute(["%{$productId}%"]);
?>
PDO、MySQLi和其他数据库库都提供预处理语句。PDO,MySQLi
SQL注入攻击主要基于利用代码未考虑安全性的漏洞。永远不要信任任何输入,尤其来自客户端的输入,即使它来自选择框、隐藏输入字段或cookie。第一个例子表明,如此简单的查询都可能造成灾难。
纵深防御策略包含几种良好的编码实践
除此之外,您还可以通过在脚本中或由数据库本身进行查询日志记录来获益,如果它支持日志记录的话。显然,日志记录无法阻止任何有害的尝试,但它有助于追溯哪些应用程序已被绕过。日志本身并没有用,但它包含的信息很有用。通常情况下,更详细的信息比更少的信息更好。
最好的方法必须是参数化查询。这样,用户输入的内容无关紧要,数据作为值传递到数据库。
网上快速搜索显示PHP中的一些可能性,这很棒!甚至在这个网站上 - https://php.net/manual/en/pdo.prepared-statements.php
这也说明了为什么这对于安全性和性能都很好。