使用mysqli_real_escape_string能否防范搜索表单SQL注入?代码求助
嘿,刚入门PHP就关注到SQL注入的问题,这意识真的很棒!先直接回答你的问题:mysqli_real_escape_string确实能在一定程度上防范SQL注入,但它不是最稳妥的方案,而且你的代码里还有不少需要调整的细节,咱们一步步拆解来看:
一、关于mysqli_real_escape_string的有效性
它对字符串类型的用户输入是有效的,但有两个关键前提:
- 你必须正确设置数据库连接的字符集(这点你的代码里漏掉了,后面会说),否则转义可能失效,依然存在注入风险;
- 它只适用于字符串参数,如果是数字类型的参数,转义逻辑就不适用了。
更推荐的方案是预处理语句(Prepared Statements)——它从根本上把SQL逻辑和用户输入分开,不需要手动转义,安全性更高,后面我会给你示例代码。
二、你的代码存在的问题
先把你的代码格式化后,逐个指出问题:
1. index.php里的问题
- 重复执行数据库查询:你一开始执行了
$sql = "SELECT * FROM anime"; $result = mysqli_query($conn, $sql); $queryresult = mysqli_num_rows($result);,随后又调用$anime = query("SELECT * FROM anime");,这完全是重复查询,浪费数据库资源,删掉前3行即可; - 未处理用户输入的空值:如果用户提交空的搜索框,
$_GET['s']存在但为空,会导致查询LIKE '%%'返回所有数据,应该先判断输入是否非空; - 输出未做XSS防护:直接
echo $a['Judul']会有XSS风险(比如标题里包含<script>标签的话会被执行),需要用htmlspecialchars()转义输出; - 未处理LIKE通配符:用户输入
%或_这类LIKE通配符时,会返回所有数据或匹配意外结果,需要转义这些字符; - 缺少查询结果为空的提示:如果搜索不到内容,页面会空着,体验不好。
2. connect.php里的问题
- 没有数据库连接错误检查:
mysqli_connect可能失败(比如密码错误、数据库不存在),需要添加错误判断; - 未设置数据库字符集:必须设置字符集为
utf8mb4(支持所有Unicode字符,包括emoji),否则mysqli_real_escape_string可能失效; - query函数缺少错误处理:如果SQL语句出错,
mysqli_query会返回false,后续mysqli_fetch_assoc会报错,需要添加错误捕获。
三、改进后的代码示例
改进版connect.php
<?php $server = "localhost"; $username = "root"; $password = ""; $databasename = "anime"; // 连接数据库并检查连接错误 $conn = mysqli_connect($server, $username, $password, $databasename); if (!$conn) { die("数据库连接失败: " . mysqli_connect_error()); } // 设置字符集为utf8mb4,确保转义有效且支持全字符 mysqli_set_charset($conn, 'utf8mb4'); function query($query) { global $conn; $result = mysqli_query($conn, $query); // 检查查询是否成功 if (!$result) { die("查询错误: " . mysqli_error($conn)); // 生产环境建议不要直接输出错误,改为记录日志 // error_log("SQL Error: " . mysqli_error($conn)); // die("服务器出错,请稍后重试"); } $rows = []; while( $row = mysqli_fetch_assoc($result) ) { $rows[] = $row; } return $rows; }
改进版index.php(保留转义方式,修复所有问题)
<?php include 'connect.php'; $anime = []; // 检查搜索参数是否存在且非空 if (isset($_GET['s']) && !empty(trim($_GET['s']))) { $search = mysqli_real_escape_string($conn, trim($_GET['s'])); // 转义LIKE通配符,避免用户输入%/_干扰搜索结果 $search = str_replace(['%', '_'], ['\%', '\_'], $search); $sql = "SELECT * FROM anime WHERE Judul LIKE '%$search%'"; $anime = query($sql); } else { // 无搜索时获取所有数据 $anime = query("SELECT * FROM anime"); } ?> <!DOCTYPE html> <html lang="en" dir="ltr"> <head> <meta charset="utf-8"> <title>Anime 搜索</title> </head> <body> <form action="" method="get"> <!-- 保留用户输入的搜索关键词,同时转义防止XSS --> <input type="text" name="s" placeholder="输入关键词搜索" value="<?php echo isset($_GET['s']) ? htmlspecialchars($_GET['s']) : ''; ?>"> <button type="submit">搜索</button> </form> <?php if (!empty($anime)) : ?> <ul> <?php foreach ($anime as $a) : ?> <!-- 转义输出,防止XSS --> <li><?php echo htmlspecialchars($a['Judul']); ?></li> <?php endforeach ; ?> </ul> <?php else : ?> <p>没有找到符合条件的内容</p> <?php endif; ?> </body> </html>
更安全的预处理语句版本(推荐)
把index.php里的搜索逻辑改成下面这样,不需要手动转义,从根源避免SQL注入:
<?php include 'connect.php'; $anime = []; if (isset($_GET['s']) && !empty(trim($_GET['s']))) { $searchPattern = '%' . trim($_GET['s']) . '%'; // 初始化预处理语句 $stmt = mysqli_prepare($conn, "SELECT * FROM anime WHERE Judul LIKE ?"); // 绑定参数("s"表示字符串类型) mysqli_stmt_bind_param($stmt, "s", $searchPattern); // 执行语句 mysqli_stmt_execute($stmt); // 获取结果集 $result = mysqli_stmt_get_result($stmt); // 提取数据 while ($row = mysqli_fetch_assoc($result)) { $anime[] = $row; } // 关闭语句 mysqli_stmt_close($stmt); } else { $anime = query("SELECT * FROM anime"); } ?>
总结
mysqli_real_escape_string在正确配置字符集的情况下对字符串参数有效,但预处理语句是更可靠的防注入方案;- 你的代码主要需要补充错误处理、字符集设置、XSS防护,同时优化查询逻辑。
内容的提问来源于stack exchange,提问作者Variel
相关产品推荐
相关产品推荐

