渗透测试-SQL注入-登录漏洞-SQL注入防护
前言
在SQL注入-登录漏洞-基础修复中我们提到这个登录页面是存在SQL漏洞的,接下来我们将说说怎么进行SQL注入防护。同时我们还会说到如何防止暴力破解。
引入
登录漏洞:登录界面可以进行SQL注入,轻易实现登录(高)
1、 从代码和SQL语句的逻辑层面进行考虑,让密码对比不轻易失效
2、 基于将用户输入的引号(单引号和双引号)进行转义处理的前提,可以用PHP的内置函数addslashes进行强制转义
另一种方法:可以使用MySQLi的预处理功能达到防SQL注入的目的。
一、登录SQL语句的逻辑问题
//先查用户名对比密码
$sql = "select * from users where username='$username'";
$result = mysqli_query($conn,$sql) or die("SQL语句执行错误"); //result获取到的查询结果,称为结果集
//修复的代码
//认证和授权失败
if(mysqli_num_rows($result) == 1){
$row = mysqli_fetch_assoc($result);
//var_dump($row); //打印
if ($md5password == $row['password']) {
echo "login-pass";
//登录成功后,记录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>";
}
二、使用addslashes函数
addslashes函数可以将字符串中的单引号、双引号、反斜杠、NULL值自动添加转义字符,从而防止SQL注入中对单引号和双引号的预防。
原始SQL语句如下:
$sql = "select * from users where username='$username' and password='$md5password'";
如果用户输入x' or userid=1#,则SQL语句变成:
$sql = "select * from users where username='x' or userid=1#' and password='$md5password'";
如果使用addslashes函数强制将用户输入的引号添加转义符,则变成:
$sql = "select * from users where username='x\' or userid=1#' and password='$md5password'";
上述SQL语句用户名为:x\' or userid=1#
三、使用mysqli面向对象方式
1.面向过程
$conn = mysqli_connect('127.0.0.1','root','你的数据库密码','learn') or die("数据库连接不成功.");
mysqli_query($conn,"set names utf8");
$sql = "select articleid,author,headline,viewcount,createtime from article where isDelete=1";
$result = mysqli_query($conn,$sql);
// 将数据库查询到的结果集中的数据取出,保存到一个数组中
//$rows = mysqli_fetch_all($result);
//mysqli_fetch_all 默认使用索引数组,也可以设定参数强制使用关联数组
$rows = mysqli_fetch_all($result,MYSQLI_ASSOC);
//遍历结果集数据并输出到页面中
foreach($rows as $row){
echo $row[0] . '-' . $row[1] . '-' . $row[2] . '-' . $row[3] . '-' . $row[4] . "<br/>";
}
2.面向对象方式
//使用mysqli面向对象方式连接数据库
function create_connection_oop(){
//连接数据库
$conn = mysqli_connect('127.0.0.1','root','你的数据库密码','learn') or die("数据库连接不成功.");
//设置编码格式的两种方式
$conn->query("set names utf8");
$conn->set_charset('utf8');
return $conn;
}
四、使用mysql预处理功能
1、 预处理功能的使用
预处理过程就是先把SQL语句交给MySQL数据库处理
//MySQLi预处理功能(面向对象)
function mysqli_prepare_stmt(){
$conn = create_connection_oop();
//?在预处理语句中用于代替参数
$sql = "update users set username = ? where userid = ?";
//实例化Prepared Statement与处理对象
$stmt = $conn->prepare($sql);
//实例化后需要将参数值进行绑定并在执行时替换
//bind_param第一个参数是数据类型 i:整数,s:字符串,d:小数,b:二进制
$stmt->bind_param("si",$username,$userid);
$username = 'hahasb';
$userid = 6;
//正式执行SQL语句
//$stmt->execute(); //execute()返回布尔类型
//$conn->commit(); //默认情况下,更新操作会自动提交,也可手工处理
//如果针对查询语句需进行结果绑定
$sql = "select * from users where userid < ?";
$stmt = $conn->prepare($sql);
$stmt->bind_param("i",$userid);
$userid = 6;
//绑定结果参数
$stmt->bind_result($userid,$username,$password,$role,$avatar,$createtime);
$stmt->execute();
//调用结果并处理
$stmt->store_result();
//输出行数
echo $stmt->affected_rows . "<br/>";
echo $stmt->num_rows . "<br/>";
//遍历结果
while ($stmt->fetch()) {
echo $userid,$username,$password,'<br/>';
}
}
mysqli_prepare_stmt();
2、 使用预处理来预防SQL注入
<?php
include "common.php";
//获得用户发送的请求
$username = $_POST['username'];
$password = $_POST['password'];
$vcode = $_POST['vcode'];
//验证码的验证
if($vcode !== '0000'){
die("vcode-error");
}
$conn = create_connection_oop();
//md5加密
$md5password = md5($password);
//拼接SQL语句并执行它
//先查用户名对比密码
$sql = "select userid,username,password,role from users where username=?";
//绑定查询参数
$stmt = $conn->prepare($sql);
$stmt->bind_param("s",$username);
//绑定结果参数
$stmt->bind_result($userid,$username2,$password2,$role);
$stmt->execute();
$stmt->store_result();
//认证和授权失败
if($stmt->num_rows == 1){
$stmt->fetch();
if ($md5password == $password2) {
echo "login-pass";
//登录成功后,记录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>";
}
//关闭数据库
$conn->close();
?>
3、 配置MySQL临时日志查看SQL语句
在MySQL中运行以下语句,开启临时日志,将日志信息保存到表格mysql数据库的general_log表中(验证防护效果)
# 开启
USE mysql;
SET GLOBAL log_output = 'TABLE';
SET GLOBAL general_log = 'ON';
# 确认
SHOW VARIABLES LIKE “general_log”;
并执行以下语句进行查询确认
SELECT * FROM general_log WHEN argument LIKE "%username%" ORDER BY thread_id DESC LIMIT 10;
完整代码见https://github.com/chengstery/security/tree/main/linux_login