如何防止用户输入中的单引号导致SQL注入漏洞?

UX-豫豪 阅读 10

我在开发登录页面时发现,当用户输入类似' OR 1=1--的恶意用户名时,后端查询直接返回了所有用户数据。虽然我在前端用了正则过滤了单引号:username.replace(/'/g, ''),但测试发现用双引号包裹输入时" OR 1=1--依然能绕过拦截。

后端PHP代码是这样拼接SQL的:


$username = $_POST['username'];
$password = $_POST['password'];
$sql = "SELECT * FROM users WHERE username = '$username' AND password = '$password'";

明明做了前端过滤为什么还是不安全?该怎么彻底防止这种注入攻击?

我来解答 赞 8 收藏
二维码
手机扫码查看
2 条解答
小菊🍀
这个问题其实暴露了两个常见的误区:一个是前端过滤并不能真正解决安全问题,另一个是直接拼接SQL语句是非常危险的行为。我们来一步步分析并解决。

首先,前端的过滤完全是形同虚设。用户输入的数据在网络传输过程中是可以被篡改的,比如通过浏览器开发者工具、Postman 或者直接抓包修改请求内容,恶意用户完全可以绕过你的前端逻辑。所以,任何安全措施都不能依赖前端,必须在后端进行严格的处理。

其次,你现在的代码中直接将用户输入拼接到SQL查询中,这种方式天生就是漏洞的温床。即使你尝试用正则表达式过滤掉单引号,攻击者仍然可以通过其他方式构造恶意输入,比如双引号、反斜杠或者其他SQL语法特性。

正确的解决方案:使用预处理语句

要彻底防止SQL注入,推荐使用预处理语句(Prepared Statements)。预处理语句的核心思想是将SQL语句的结构和数据分离,数据库会先解析SQL语句的结构,然后再绑定具体的参数值。这样可以确保用户输入的内容不会被当作SQL代码执行。

以下是改进后的PHP代码示例:


// 使用PDO扩展连接数据库
$dsn = 'mysql:host=your_host;dbname=your_db;charset=utf8';
$username_db = 'your_username';
$password_db = 'your_password';

try {
// 创建PDO实例
$pdo = new PDO($dsn, $username_db, $password_db);
// 设置错误模式为异常
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

// 获取用户输入
$user_input_username = $_POST['username'];
$user_input_password = $_POST['password'];

// 准备SQL语句,使用占位符
$sql = "SELECT * FROM users WHERE username = :username AND password = :password";
$stmt = $pdo->prepare($sql);

// 绑定参数到占位符,确保输入被正确转义
$stmt->bindParam(':username', $user_input_username);
$stmt->bindParam(':password', $user_input_password);

// 执行查询
$stmt->execute();

// 获取结果
$result = $stmt->fetchAll(PDO::FETCH_ASSOC);

if (count($result) > 0) {
echo "登录成功";
} else {
echo "用户名或密码错误";
}
} catch (PDOException $e) {
// 捕获异常并输出错误信息
echo "数据库错误: " . $e->getMessage();
}


为什么这样做能解决问题?

1. 参数化查询:在预处理语句中,SQL语句的结构和数据是分开的。无论用户输入什么内容,都会被当作普通字符串处理,而不会被解析为SQL代码。
2. 自动转义:PDO会根据数据库的要求对绑定的参数进行适当的转义,避免特殊字符引发问题。
3. 安全性更高:即使攻击者尝试输入恶意内容,比如 ' OR 1=1--,这些内容也会被当作普通字符串处理,不会影响SQL语句的逻辑。

需要注意的是,如果你还在使用老式的 mysqli 扩展,也可以用它的预处理功能,不过我个人更推荐PDO,因为它支持多种数据库,并且API更加简洁。

其他注意事项

1. 密码存储:从你的代码来看,密码是直接明文存储的,这是非常不安全的做法。建议使用 password_hash()password_verify() 来处理密码的加密和验证。
- 存储时:password_hash($password, PASSWORD_DEFAULT)
- 验证时:password_verify($input_password, $hashed_password)

2. 最小权限原则:确保你的数据库用户只有执行必要操作的权限,比如只允许查询特定表,而不允许删除或修改其他表。

3. 日志记录:可以记录失败的登录尝试,方便后续排查潜在的攻击行为。

总之,永远不要相信用户的输入,也不要试图自己去过滤或转义数据,交给专业的工具(如PDO)来处理才是最稳妥的方式。
点赞 1
2026-02-17 18:29
皇甫乙涵
前端过滤根本防不住SQL注入,因为请求可以被轻易伪造,直接绕过你的前端代码。真正安全的做法是后端必须正确处理输入数据。你现在的代码拼接SQL字符串,这本身就是个大坑,赶紧改掉。

在WordPress里,推荐用 $wpdb->prepare() 方法来处理SQL查询。这个方法会自动转义特殊字符,防止SQL注入。把你的代码改成这样:


global $wpdb;
$username = $_POST['username'];
$password = $_POST['password'];

$sql = $wpdb->prepare(
"SELECT * FROM {$wpdb->prefix}users WHERE username = %s AND password = %s",
$username,
$password
);

$results = $wpdb->get_results($sql);


这里的关键点是用 %s 占位符,$wpdb->prepare() 会帮你处理转义。另外要注意,WordPress自带的用户认证其实更安全,建议直接用 wp_authenticate() 钩子函数来处理登录验证:


$user = wp_authenticate($_POST['username'], $_POST['password']);
if (is_wp_error($user)) {
// 登录失败处理
} else {
// 登录成功逻辑
}


顺带一提,密码明文存储和对比也是个大问题,记得用 wp_hash_password()wp_check_password() 来处理密码加密验证。安全问题上可不能偷懒,不然分分钟被黑了都不知道怎么死的。
点赞
2026-02-15 10:01