一、session验证码存在的漏洞
当验证码保存在session变量中,每一次刷新登录页面,会重新访问一次vcode.php,但是如果不刷新页面,
而是直接通过python、fiddle、burp等直接发送登录请求,则此时验证码在session中保持最后一个,
从而只需要在发送登录请求的时候将验证码设置为最后一个即可。
所有,验证码每使用一次必须清空,不允许继续给下一次使用
二、session验证防护
验证码只允许使用一次,每用一次,就需要清空,让用户不得不再次请求vcode.php,获取新的验证码
if(strtoupper($_SESSION['vcode']) != strtoupper($vcode)){
die("vcode-error");
}
else{
// 验证码输入成功后,再清空本次session中的验证码
unset($_SESSION['vcode']);
// $_SESSION['vcode']=''; 不能直接设置为空字符串,否则也可以在请求上提交一个空验证码,实现绕过
}
三、避免使用cookie的验证码
session的生成过程,当用户第一次访问服务器时,如果请求中没有带Cookie字段,
则服务器会在首次调用session_start()的页面中响应一个Session ID,默认命名为:PHPSESSID,响应字段值如下:
set-cookie: //也可以理解为系统自动生成的
接下来,后续的每一个请求,将会在请求头的cookie字段中添加session ID,目的是主动告诉服务器,我是谁。
cookie: PHPSESSID=
另外,在服务器端也可以直接
验证码漏洞的防护
<?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 from user where username=?";
// 基于面向对象和MySQL预处理功能是实现MySQL注入
$stmt = $conn->prepare($sql);
echo ($sql);
$stmt->bind_param("s",$username); // 绑定结果集
$stmt->bind_result($userid,$username2,$passwrod2,$role); //绑定结果参数
$stmt->execute();
$stmt->store_result(); //获取行数
//$result = mysqli_query($conn,$sql) or die("SQL语句执行错误"); // $result获取到的查询结果,称为结果集
// 以下代码没有进行爆破的防护,违背了owasp认证和授权失败
// 获取到的行数是否为一
if($stmt->num_rows == 1 ){
$stmt->fetch();
if($password == $passwrod2){
echo "login-pass";
// 失效的访问控制
// url绕过
// 登录成功后,记录session变量
$_SESSION['username'] = $username;
$_SESSION['islogin'] = 'true';
echo " <script>location.href='welcome.php'</script> ";
}
else{
echo "login-fail";
echo " <script>location.href='login.html'</script> ";
}
}
else{
echo "login-fail";
echo " <script>location.href='login.html'</script> ";
}
// 关闭数据库
mysqli_close($conn);
?>
// 基于面向对象和MySQL预处理功能是实现MySQL注入
$stmt = $conn->prepare($sql);
echo ($sql);
$stmt->bind_param("s",$username); // 绑定结果集
$stmt->bind_result($userid,$username2,$passwrod2,$role); //绑定结果参数
$stmt->execute();
$stmt->store_result(); //获取行数
//$result = mysqli_query($conn,$sql) or die("SQL语句执行错误"); // $result获取到的查询结果,称为结果集
对基于面向对象的代码进行解析
这段代码是一个PHP代码片段,用于执行数据库查询并获取结果集的行数。具体来说,它执行以下操作:
- $conn->prepare($sql)
:这行代码使用数据库连接对象 $conn 的 prepare() 方法来准备一个 SQL 查询语句。$sql 是包含 SQL 查询语句的字符串变量。
- echo ($sql)
:这行代码将 SQL 查询语句的内容输出到屏幕上,以便调试和验证查询语句是否正确。
- $stmt->bind_param("s", $username)
:这行代码使用准备好的语句对象 $stmt 的 bind_param() 方法,将变量 $username 绑定到查询语句中的参数占位符。这里使用的是字符串类型的参数占位符 "s"。
- $stmt->bind_result($userid, $username2, $password2, $role)
:这行代码使用 bind_result() 方法将查询结果的列与变量进行绑定。这里将查询结果的列分别绑定到 $userid、$username2、$password2 和 $role 这些变量上。
- $stmt->execute()
:这行代码执行准备好的查询语句。
- $stmt->store_result()
:这行代码将查询结果存储在客户端的内存中,以便后续可以获取结果集的行数。
总之,这段代码的目的是准备并执行一个数据库查询语句,并将查询结果的行数存储在客户端的内存中,以便后续使用。