万能密码的SQL注⼊漏洞其PHP环境搭建及代码详解+防御⼿段⽬录
环境搭建
这个渗透环境的搭建有以下⼏点
基于session的会话
登录界⾯
登录成功界⾯
注销界⾯
数据库搭建
数据库连接
session会话
服务器端利⽤session_start()函数发起⼀次session的会话
此时我们登录成功后⽤户的数据被保存在服务器端的Cookie: session= ,即sessionID
如果需要再次访问
服务器端的$_SESSION['...']会获取⽤户session
然后与原本存在于服务器的sessionID进⾏⽐对,如果⽐对成功,则证明⽤户正确
环境搭建代码
创建数据库脚本
在MySQL中使⽤source命令即可运⾏脚本:
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');
登录界⾯html:
<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>
查询数据库是否为正确的账号密码php代码
<?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('登录失败!');(-1)</script>";
}
mysqli_close($con);
>
连接数据库php代码:
<?php
$con=mysqli_connect('127.0.0.1','root','root') or die("数据库连接失败!");
mysqli_select_db($con,'lab')or die("数据库连接失败");
>
注销登录代码(即关闭session会话)
<?php
session_start();
session_unset();
session_destroy();
echo "注销成功";
>
登录成功欢迎界⾯:
<?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'
万能密码攻击防护
使⽤正则表达式限制⽤户输⼊:
可以使⽤正则表达式限制⽤户的⽤户名输⼊,⽐如:/^[a-z0-9A-Z_]{5,16}$/
这个限制了⽤户5位以上16位以下的字母数字下划线为⽤户名的输⼊
这个限制在check_login.php中添加
<?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));php项目搭建
if ($row=mysqli_fetch_array($query)){
session_start();
$_SESSION['username']=$row[1];
echo "<a href='welcome.php'>欢迎访问</a>";
}else{
echo "<script>alert('登录失败!');(-1)</script>";
}
mysqli_close($con);
}
>
使⽤PHP转义函数:
addslashes()函数:
能够将单引号、双引号、反斜杠和null转义
mysql_escape_string()函数、mysql_real_escape_string()函数
这个是转义SQL语句中的符号,php7.x版本的都要变成mysqli
$username=isset($_POST['username'])?addslashes($_POST['username']):'';
$password=isset($_POST['password'])?mysqli_real_escape_string($con,$_POST['password']):'';转义函数的弊端
因为使⽤的是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
<?php
include('con_database.php');
$username=isset($_POST['username'])?$_POST['username']:'';
$password=isset($_POST['password'])?$_POST['password']:'';
if($username==''||$password==''){
echo "<script>alert('错误!');(-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('登录失败!!');(-1);</script>";
}
$stmt->close();
}
$con->close();
>
⼀些内容已经标记在代码的注释⾥⾯
参数化的PHP代码真的能够很有效地防⽌SQL注⼊。
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论