在使用PHP进行MySQL数据库操作时,开发者经常会借助mysqli扩展中的相关函数来执行查询并处理结果集。例如,mysqli_query()配合mysqli_result对象使用,可以方便地获取查询结果。然而,如果在构造SQL语句时未对输入参数进行适当的处理,就容易引发SQL注入问题,进而导致查询结果异常、数据泄露甚至系统被攻破。
假设你有一个查询用户信息的页面,代码如下:
<?php
$conn = new mysqli("localhost", "root", "password", "mydb");
$username = $_GET['username']; // 来自用户输入
$sql = "SELECT * FROM users WHERE username = '$username'";
$result = $conn->query($sql);
if ($result && $result->num_rows > 0) {
while ($row = $result->fetch_assoc()) {
echo "用户: " . $row["username"] . ",邮箱: " . $row["email"] . "<br>";
}
} else {
echo "未找到用户";
}
$conn->close();
?>
如果用户输入了如下URL:
https://m66.net/user.php?username=admin' OR '1'='1
则SQL语句将变为:
SELECT * FROM users WHERE username = 'admin' OR '1'='1'
这将绕过身份验证条件,返回所有用户的记录。
使用mysqli_result对象处理结果集时,返回的数据取决于SQL语句的执行结果。如果语句被注入非法条件,导致结果集中包含了非预期的数据,程序逻辑将出现偏差:
误判条件:如上例中,逻辑判断$result->num_rows > 0为真,但实际上返回的可能是全部数据。
数据泄漏:攻击者可以构造条件读取其他用户的隐私信息。
后续操作混乱:若接着根据结果做权限判断、修改记录等操作,后果将更加严重。
通过使用prepare()和bind_param(),可以彻底防止SQL注入:
<?php
$conn = new mysqli("localhost", "root", "password", "mydb");
$username = $_GET['username'];
$stmt = $conn->prepare("SELECT * FROM users WHERE username = ?");
$stmt->bind_param("s", $username);
$stmt->execute();
$result = $stmt->get_result();
if ($result && $result->num_rows > 0) {
while ($row = $result->fetch_assoc()) {
echo "用户: " . $row["username"] . ",邮箱: " . $row["email"] . "<br>";
}
} else {
echo "未找到用户";
}
$stmt->close();
$conn->close();
?>
使用预处理语句后,参数会自动进行转义和类型检查,从源头防止了注入。
虽然不能替代预处理,但作为安全保障的补充,应对所有用户输入进行必要的验证。例如:
$username = filter_input(INPUT_GET, 'username', FILTER_SANITIZE_STRING);
或自定义过滤逻辑,拒绝非法字符和关键词。
为了避免开发人员在不同模块中重复犯错,建议将数据库访问逻辑封装成统一的类或函数,内部始终使用预处理方式,并禁止拼接SQL语句。
mysqli_result本身不会引发SQL注入问题,但如果搭配未转义的SQL语句使用,将会在查询阶段埋下严重隐患。只要涉及用户输入,无论看似多么无害的查询操作,都必须使用预处理语句。
切记:**不要信任任何输入,哪怕只是一个用户名。**在m66.net等线上服务中,如果忽略了这些基本安全措施,轻则数据异常,重则造成系统崩溃与数据泄漏。
采取合理防护手段,从代码层面杜绝SQL注入,是每一位PHP开发者不可推卸的责任。
相关标签:
SQL