万能密码的SQL注入漏洞其PHP环境搭建及防御手段
一、环境搭建
这个渗透环境的搭建有以下几点:
- 基于session的会话
- 登录界面
- 登录成功界面
- 注销界面
- 数据库搭建
- 数据库连接
二、session会话
- 服务器端利用session_start()函数发起一次session的会话
- 此时我们登录成功后用户的数据被保存在服务器端的Cookie: session= ,即sessionID
- 如果需要再次访问
-
服务器端的
$_SESSION['...']
会获取用户session - 然后与原本存在于服务器的sessionID进行比对,如果比对成功,则证明用户正确
三、环境搭建代码
1、创建数据库脚本
在MySQL中使用source命令即可运行脚本:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
drop database if exists lab; create database lab; use lab; create table users ( id int not null auto_increment, username char (32) not null , passcode char (32) not null , primary key (id) ); insert into users(username,passcode) values ( 'admin' , 'admin123' ); insert into users(username,passcode) values ( 'alice' , 'alice456' ); |
2、登录界面html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
|
<html> <head> <meta charset= "UTF-8" > <title>Login</title> <style> #a { width: 500px; text-align: center; } .b { width: 200px; height: 30px; } </style> </head> <body> <div id=a> <h2>Login!</h2> <form name = "form_login" method= "POST" action = "check_login.php" > Username:<input type= "text" class= "b" name = "username" /><br> <br> Password :<input type= "password" class= "b" name = "password" /><br> <input type= "submit" name = "Submit" value= "Submit" /> <input type= "reset" name = "reset" value= "Reset" /> </form> </div> </body> </html> |
3、查询数据库是否为正确的账号密码php代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
<?php include( 'con_database.php' ); $username=isset($_POST[ 'username' ])?$_POST[ 'username' ]: '' ; $ password =isset($_POST[ 'password' ])?$_POST[ 'password' ]: '' ; if($username== '' || $ password == '' ){ echo "<script>alert('请输入账号和密码!')</script>" ; exit; } $sql= "select * from users where username='$username' and passcode='$password'" ; $query=mysqli_query($con,$sql) or die( 'SQL语句执行失败' .mysqli_error($con)); if ($row=mysqli_fetch_array($query)){ session_start(); $_SESSION[ 'username' ]=$row[1]; echo "<a href='welcome.php'>欢迎访问</a>" ; } else { echo "<script>alert('登录失败!');history.go(-1)</script>" ; } mysqli_close($con); ?> |
4、连接数据库php代码:
1
2
3
4
|
<?php $con=mysqli_connect( '127.0.0.1' , 'root' , 'root' ) or die( "数据库连接失败!" ); mysqli_select_db($con, 'lab' ) or die( "数据库连接失败" ); ?> |
5、注销登录代码(即关闭session会话)
1
2
3
4
5
6
|
<?php session_start(); session_unset(); session_destroy(); echo "注销成功" ; ?> |
6、登录成功欢迎界面
1
2
3
4
5
6
7
8
9
10
|
<?php session_start(); if(isset($_SESSION[ 'username' ])){ echo "欢迎用户" .$_SESSION[ 'username' ]. "登录" ; echo "<br>" ; echo "<a href=logout.php>退出登录</a>" ; } else { echo "您没有权限访问" ; } ?> |
至此,我们的渗透环境就构建好了
四、万能密码漏洞剖析
-
用户名输
入' or 1=1 or'
,密码随意,发现可以登录进去 -
密码输入
'or '1=1
也可以登录进去
当然登录方法不止一种:
原来查询语句是这样的:
$sql="select * from users where username='$username' and passcode='$password'";
经过注入之后,变成:
$sql="select * from users where username='' or 1=1 or ' and passcode='****'";
我们观察到,where后面呃字句中的username被闭合,并且字句分成三个句子并用or连接。
在SQL语句中 and的优先级要大于or,所以1=1先判断,为真,即where后面的语句为真,即整个SQL语句为真,即表示查询正确
而形成的语句可以将整个users表查询,后面的$row=mysqli_fetch_array($query)
选择的是查询的第一行值,这样满足了SQL语句并跳过了登录验证
由此可以引申出,只要where后面字句为真,即可跳过验证,有如下衍生方法:
-
' or 1=1 #
-
' or 1=1 --
(后面有空格) -
'or"="or'
五、万能密码攻击防护
1、使用正则表达式限制用户输入
可以使用正则表达式限制用户的用户名输入,比如:/^[a-z0-9A-Z_]{5,16}$/
这个限制了用户5位以上16位以下的字母数字下划线为用户名的输入
这个限制在check_login.php中添加
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
|
<?php include( 'con_database.php' ); $username=isset($_POST[ 'username' ])?$_POST[ 'username' ]: '' ; $ password =isset($_POST[ 'password' ])?$_POST[ 'password' ]: '' ; if (!preg_match( "/^[a-Z0-9A-Z_]{5,16}$/" ,$username)){ echo "<script>alert('用户名格式错误')</script>" ; exit; if($username== '' || $ password == '' ){ echo "<script>alert('请输入账号和密码!')</script>" ; exit; } $sql= "select * from users where username='$username' and passcode='$password'" ; $query=mysqli_query($con,$sql) or die( 'SQL语句执行失败' .mysqli_error($con)); if ($row=mysqli_fetch_array($query)){ session_start(); $_SESSION[ 'username' ]=$row[1]; echo "<a href='welcome.php'>欢迎访问</a>" ; } else { echo "<script>alert('登录失败!');history.go(-1)</script>" ; } mysqli_close($con); } ?> |
2、使用PHP转义函数
- addslashes()函数:能够将单引号、双引号、反斜杠和null转义
- mysql_escape_string()函数、mysql_real_escape_string()函数这个是转义SQL语句中的符号,php7.x版本的都要变成mysqli
1
2
|
$username=isset($_POST[ 'username' ])?addslashes($_POST[ 'username' ]): '' ; $ password =isset($_POST[ 'password' ])?mysqli_real_escape_string($con,$_POST[ 'password' ]): '' ; |
3、转义函数的弊端
因为使用的是UTF-8编码,不是宽字节编码,形成的'会被变成%5c%27
Windows下默认的是宽字节的gbk编码
如果在%5c前面加上一个字符形成一个复杂的汉字,那么单引号仍然会被输出
六、MySQLi 参数化查询
在使用参数化查询的情况下,服务器不会将参数的内容是为SQL指令中的一部分
而是在数据库完成SQL指令的编译之后,再代入参数运行
此时就算参数里面有恶意数据
但是此时SQL语句以及编译完成
就不会被数据库运行
PHP提供了三种访问mysql数据库的拓展:
- MySQL (PHP5.5起,已经废除)
- MySQLi
- PDO(PHP Data Object PHP数据对象)
PDO和MySQLi提供面向对象的api
MySQLi也存在面向过程的api,所以容易从MySQL转换到MySQLi
下面是mysqli形式的check_login.php 写法,新建check_login_mysqli.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
|
<?php include( 'con_database.php' ); $username=isset($_POST[ 'username' ])?$_POST[ 'username' ]: '' ; $ password =isset($_POST[ 'password' ])?$_POST[ 'password' ]: '' ; if($username== '' ||$ password == '' ){ echo "<script>alert('错误!');history.go(-1);</script>" ; exit; } $sql= "select * from users where username=? and passcode=? ;" ;//问号表示需要一个参数 $stmt=$con-> prepare ($sql);//预编译SQL语句 if(!$stmt){ echo 'prepare 执行错误' ; } else { $stmt->bind_param( "ss" ,$username,$ password ); //为预编译绑定SQL参数,ss表示两个字符串 //i—— int d—— double s——string b——boolean $stmt-> execute (); $result=$stmt->get_result(); $row=$result->fetch_row(); if($row){ session_start(); $_SESSION[ 'username' ]=$row[1]; echo $row[1]. "<a href='welcome.php'>欢迎访问</a>" ; } else { echo "<script>alert('登录失败!!');history.go(-1);</script>" ; } $stmt-> close (); } $con-> close (); ?> |
一些内容已经标记在代码的注释里面
参数化的PHP代码真的能够很有效地防止SQL注入。
以上就是万能密码的SQL注入漏洞其PHP环境搭建及防御手段的详细内容,更多关于万能密码的SQL注入 PHP环境搭建 防御手段的资料请关注服务器之家其它相关文章!
原文链接:https://www.cnblogs.com/Zeker62/p/15216324.html