如何防止用户输入中的单引号导致SQL注入漏洞?
我在开发登录页面时发现,当用户输入类似' 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'";
明明做了前端过滤为什么还是不安全?该怎么彻底防止这种注入攻击?
首先,前端的过滤完全是形同虚设。用户输入的数据在网络传输过程中是可以被篡改的,比如通过浏览器开发者工具、Postman 或者直接抓包修改请求内容,恶意用户完全可以绕过你的前端逻辑。所以,任何安全措施都不能依赖前端,必须在后端进行严格的处理。
其次,你现在的代码中直接将用户输入拼接到SQL查询中,这种方式天生就是漏洞的温床。即使你尝试用正则表达式过滤掉单引号,攻击者仍然可以通过其他方式构造恶意输入,比如双引号、反斜杠或者其他SQL语法特性。
正确的解决方案:使用预处理语句
要彻底防止SQL注入,推荐使用预处理语句(Prepared Statements)。预处理语句的核心思想是将SQL语句的结构和数据分离,数据库会先解析SQL语句的结构,然后再绑定具体的参数值。这样可以确保用户输入的内容不会被当作SQL代码执行。
以下是改进后的PHP代码示例:
为什么这样做能解决问题?
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)来处理才是最稳妥的方式。
在WordPress里,推荐用
$wpdb->prepare()方法来处理SQL查询。这个方法会自动转义特殊字符,防止SQL注入。把你的代码改成这样:这里的关键点是用
%s占位符,$wpdb->prepare()会帮你处理转义。另外要注意,WordPress自带的用户认证其实更安全,建议直接用wp_authenticate()钩子函数来处理登录验证:顺带一提,密码明文存储和对比也是个大问题,记得用
wp_hash_password()和wp_check_password()来处理密码加密验证。安全问题上可不能偷懒,不然分分钟被黑了都不知道怎么死的。