为何PHP登录表单无法触发SQL Injection?无防护却失效
我完全懂你的困惑——特意做了个没防护的登录表单,结果常用的注入payload却死活不管用,一直跳凭据错误。咱们一步步扒代码、理逻辑,把问题找出来。
1. SQL逻辑优先级直接坑了你的Payload
先看你用的admin'OR'1'='1这个payload,假设你把它填在用户名框,密码随便输个比如test,咱们拼一下最终生成的SQL语句:
你的代码里的SQL模板是:
$sql="SELECT * FROM users WHERE username='$username' AND password='$password'";
替换后会变成:
SELECT * FROM users WHERE username='admin'OR'1'='1' AND password='test'
这里的核心问题是SQL逻辑运算符的优先级:AND的优先级比OR高,所以这条SQL会被数据库解析成:
SELECT * FROM users WHERE (username='admin') OR ('1'='1' AND password='test')
也就是说,只有两种情况会匹配到数据:
- 数据库里真的有个叫
admin的用户(不管密码是什么); - 你输的密码刚好是
test(因为'1'='1'永远为真,这部分等价于直接判断密码)。
如果你的数据库里没有admin用户,或者你输的密码不对,这条SQL就会返回0行,自然触发“凭据错误”。
怎么调整Payload才能生效?
你需要修改payload来改变逻辑优先级,或者直接注释掉后面的密码判断:
- 方案1:用户名填
admin' OR '1'='1' --(注意最后有空格),拼接后的SQL会变成:
后面的SELECT * FROM users WHERE username='admin' OR '1'='1' -- ' AND password='test'AND password='test'会被SQL注释掉,整个条件变成username='admin' OR '1'='1'(永远为真),就能匹配到用户数据。 - 方案2:用户名直接填
' OR '1'='1' --,不管密码填什么,都能直接让条件成立。
2. 可能存在的隐形防护因素
虽然你说没设置防护,但有些隐形的转义机制可能在生效:
自动转义的问题
如果你的myconnection.php里开启了字符集转义(比如mysqli_set_charset($db, 'utf8mb4');其实会自动处理部分转义?不对,准确说如果用了mysqli_real_escape_string但你没注意到,或者服务器上开启了早已废弃的magic_quotes_gpc(旧PHP环境可能存在),那么你输入的单引号会被自动转义成\',导致payload失效。
比如输入admin'OR'1'='1会被转义成admin\'OR\'1\'=\'1,拼接后的SQL变成:
SELECT * FROM users WHERE username='admin\'OR\'1\'=\'1' AND password='test'
这时候单引号被转义,payload就变成了普通字符串,没法破坏SQL结构。你可以加一行调试代码看看实际的$username值:
var_dump($username); // 放在$username=$_POST['username'];后面
如果输出里的单引号变成了\',说明存在自动转义。
数据库表为空
如果你的users表压根没有任何数据,那不管SQL条件写得多么离谱,都会返回0行,自然触发错误提示。你可以先手动插一条测试数据:
INSERT INTO users (username, password) VALUES ('testuser', 'testpass');
再测试注入,看看会不会生效。
3. 代码里的小bug(不影响注入,但值得修正)
顺便提几个和注入无关但影响功能的小问题:
$login_user=$_POST["$username"];这里应该写成$_POST['username'],你现在是用$username的值作为键去取POST数据,会导致错误;- 你的JavaScript提示有语法错误:
echo "<script='text/javascript'>$message</script>";应该改成echo "<script type='text/javascript'>alert('$message');</script>";,否则不会弹出提示; - 登录成功跳转后要加
exit;或者die();,避免后面的代码继续执行。
内容的提问来源于stack exchange,提问作者BRond

