一、基本思路
在前述攻防的过程中,我们从最开始写一个简单的登录页面和后台功能开始,
到发现SQL注入漏洞,万能验证码,爆破等漏洞开始,
一路修改代码对漏洞进行防护,一路又使用新的手段进行破解,最终只剩下爆破这一个漏洞了,
只要限制用户登录失败的次数,那么就可以从根本上防止暴力破解
1)需要在数据库中记录某个用户的登录失败的次数,每登录失败一次,则+1
2)需要在数据库中记录某个用户最后一次登录时间,用于判断是否接触限制
3)每一次登录时,只要用户名是存在的,都需要从数据库里面取得这两个值,从而进行判断
二、技术准备
在数据库中添加两列,failcount,lasttime,如下图所示
1.核心的SQL语句操作
同时更新两个字段并更新时间为现在:update user set failcount=failcount+1 lasttime=now() where userid=?
比较两个时间的差值,单位可以是分或者是秒等:select lasttime,now(),TIMSTAMPDIFF(MINUTE,lasttime,NOW()) from user;
2.在php中处理时间
完整代码展示-利用登录次数的限制从而达到防止爆破的漏洞
<?php
/**
* 本代码解决用户登录失败的限制问题,更加完整的解决登录模块的爆破和其他漏洞
*/
include "common.php";
// 获取用户提交的登录请求的数据
// 使用addslashes函数对用户名进行转义
$username = $_POST['username'];
$password = $_POST['password'];
$vcode = $_POST['vcode'];
// 验证码的验证
// 此处启用了万能验证码的漏洞==》存在安全漏洞:认证和授权失败
// if($vcode !== '0000'){
// die("vcode-error");
// }
// 根据图片验证码进行验证
if(strtoupper($_SESSION['vcode']) != strtoupper($vcode)){
die("vcode-error");
}
else{
// 验证码输入成功后,再清空本次session中的验证码
unset($_SESSION['vcode']);
// $_SESSION['vcode']=''; 不能直接设置为空字符串,否则也可以在请求上提交一个空验证码,实现绕过
}
// 无论验证码是否正确,再提交一次请求均清空一次,但是会增加服务器的负担
unset($_SESSION['vcode']);
// if(strtoupper($_COOKIE['vcode']) !=strtoupper($vcode)){
// die("vcode-error");
// }
$conn=create_connection_oop();
// create_connection_oop()
// create_connection()是调用common.php中定义的函数,就是数据库连接的
// $conn = create_connection();
$sql= "select userid,username,password,role,failcount,TIMESTAMPDIFF(Minute,lasttime,now()) from user where username=?";
// 基于面向对象和MySQL预处理功能是实现MySQL注入
$stmt = $conn->prepare($sql);
$stmt->bind_param("s",$username); // 绑定结果集
$stmt->bind_result($userid,$username2,$passwrod2,$role,$failcount,$timediff); //绑定结果参数 $timmdiff得到了相差的分钟数
$stmt->execute();
$stmt->store_result(); //获取行数
// 获取到的行数是否为一,只表示用户名存在
if($stmt->num_rows == 1 ){ //表明用户存在
$stmt->fetch();
// 判断密码是否正确之前,先判断登录次数是否受限
if ($failcount >= 5 && $timediff <=60){
die('user-locked');
}
//判断密码是否正确之前,先判断登录次数是否受限
if ($failcount>=5){
die('user-locked');
}
if($password == $password2){
// 先把failcount重置为零
if($failcount > 0){
$sql = "update user set failcount=0 where userid=?";
$stmt = $conn->prepare($sql);
$stmt->bind_param("i",$userid);
$stmt->execute();
}
echo "login-pass";
// 失效的访问控制
// url绕过
echo "hello,word";
// 登录成功后,记录session变量
$_SESSION['username'] = $username;
$_SESSION['islogin'] = 'true';
echo " <script>location.href='welcome.php'</script> ";
}
else{
$sql = "update user set failcount=failcount+1,lasttime=now() where userid=?";
$stmt = $conn->prepare($sql);
$stmt->bind_param("i",$userid);
$stmt->execute();
echo "login-fail";
echo " <script>location.href='login.html'</script> ";
}
}
else{
echo "user-invalid";
echo " <script>location.href='login.html'</script> ";
}
// 关闭数据库
mysqli_close($conn);
?>
登录模块的各类的防护措施“
验证码:不建议使用图片验证码
登录次数的限制,并记录IP
多因素认证:账密+短信
记录用户常用IP地址区域,如果在不常用区域登录,预警
除了使用session外,也会使用到token