当前位置:首页 > 时尚

sql注入万能密码

很多人第一次听说“万能密码”,入万是入万在某个论坛的帖子里,或者某部黑客题材的入万影视剧里。输入 ' or 入万1=1 --,回车,入万登录成功。入万屏幕跳转进后台,入万像变魔术一样。入万但这事没那么玄,入万它背后是入万一条再简单不过的SQL语句被改写了逻辑。

先看一个典型的入万登录验证代码,PHP写的入万,很老派的入万那种:

$sql = "SELECT * FROM users WHERE username = '$username' AND password = '$password'"; $result = mysql_query($sql); if (mysql_num_rows($result) > 0) {  // 登录成功 }

假设你在用户名框里填入 ' or 1=1 --,密码框随便写。入万拼出来的入万SQL就变成了:

SELECT * FROM users WHERE username = '' or 1=1 --' AND password = '随便写'

拆开看。username = '' 是假,但后面跟着 or 1=1,整个WHERE条件恒为真。两个连字符 -- 在SQL里是注释符,它把后面的 ' AND password = '随便写' 全部注释掉了。数据库返回users表里的所有行,mysql_num_rows 自然大于0,登录通过。

这就是万能密码的全部原理。没有漏洞利用链,没有内存溢出,就是一加一等于二的事。

但现实里能不能用,得看条件。我见过不少人拿着 ' or 1=1 -- 去试各种网站,大多数时候失败。原因有几个:

注释符不对。MySQL里 -- 后面必须跟一个空格,写成 -- 直接贴下一个字符,有些版本不认。更通用的写法是 #,但 # 在URL里是锚点,得编码成 %23。SQL Server用的是 --,Oracle也认 --。不同数据库,语法细节不一样。

引号闭合问题。上面的例子假设username字段用单引号包裹。如果代码里用的是双引号,或者用了PDO预处理,那这套就废了。预处理语句会把 ' or 1=1 -- 当成一个普通的字符串去匹配username字段,数据库里没有哪个用户叫这个名字,返回空结果。

还有一个经常被忽略的点:很多登录逻辑不是直接查用户表,而是先查用户名,拿到哈希后的密码,再在PHP层面用 password_verify() 比对。这种情况下,SQL注入即使绕过了查询,也过不了密码校验那一关。

我早年在一个小型CMS上实测过。后台登录页,用户名输入 admin' --,密码留空。SQL拼接后变成:

SELECT * FROM admin WHERE username = 'admin' --' AND password = ''

直接以admin身份登录。那个CMS没有用预处理,密码字段甚至是明文存储。这种系统在2010年前后遍地都是,现在少了很多,但没绝迹。某些物联网设备的后台、老旧的企业内网系统、学校教务的某个角落,偶尔还能碰到。

万能密码的本质不是“密码”,是SQL语法注入。它利用的是开发者把用户输入直接拼进SQL语句这个坏习惯。防御方法也简单:用参数化查询,别拼字符串。PHP的PDO、Java的PreparedStatement、Python的sqlite3参数绑定,都能根治。

有个细节值得说。很多人以为 or 1=1 是万能密码的核心,其实 or '1'='1' 也一样,or 'a'='a' 也行。关键是构造一个恒真条件,然后注释掉后面的验证逻辑。甚至不需要 1=1,or 1 在MySQL里也返回真,因为非零整数被当作true。

最后提一句。现在还在用 ' or 1=1 -- 去扫站的人,大概率扫不到什么。WAF会拦,预处理会挡,前端可能还有各种校验。但这个知识点本身不过时——它让你理解SQL语句是怎么被拆解和重组的。理解了这一层,再看其他注入类型,比如联合查询、报错注入、盲注,思路就通了。

魔术不复杂,复杂的是魔术师的手法和观众的误解。

分享到: