SQL注入-登录漏洞-SQL注入防护
引入:登录漏洞:登录页面可以进行SQL注入,进而轻易实现登录(高)
1.从代码和SQL语句的逻辑层面进行考虑,不能轻易让密码对比失效。
2.基于将用户输入的引号(单引号和双引号)进行转义处理的前提,可以使用PHP内置函数addslashes进行强制转义。
另外一种方法:可以使用MySQLi的预处理功能,也可以达到SQL的注入。
3.
一、登录SQL语句的逻辑问题
$sql= "select * from user where username='x' or use_id =1#'and password='$password'";
在SQL注入后打乱了SQL的语句拼接,重新闭合成了一个新的SQL语句
该SQL语句实现登录操作时,存在严重的逻辑问题,用户名和密码的对比不应该放在同一条SQL语句
应该先通过用户名查询user表,如果确实找到一条记录(用户名唯一的情况下),找到记录以后在进行密码的单独对比
// $sql= "select * from user where username='x'or use_id =1#'and password='$password'";
// 在SQL注入后打乱了SQL的语句拼接,重新闭合成了一个新的SQL语句
// 该SQL语句实现登录操作时,存在严重的逻辑问题,用户名和密码的对比不应该放在同一条SQL语句
// 应该先通过用户名查询user表,如果确实找到一条记录(用户名唯一的情况下),找到记录以后在进行密码的单独对比
//
// $sql= "select * from user where username='$username'and password='$password'";
$sql= "select * from user where username='$username'";
$result = mysqli_query($conn,$sql) or die("SQL语句执行错误"); // $result获取到的查询结果,称为结果集
// 以下代码没有进行爆破的防护,违背了owasp认证和授权失败
//如果用户名真实存在,刚好找到一条,则再单独进行密码的比较,
//即使用户名出现sql注入漏洞,但是只要密码不正确,也无法登录
if(mysqli_num_rows($result) == 1 ){
// 密码的单独的对比
// mysqli_fetch_all是关联数组
// $row = mysqli_fetch_all($result,MYSQLI_ASSOC);
// assoc对应当前游标指向的这一行,以关联数组的方式来书写
$row = mysqli_fetch_assoc($result);
// 进行判断输入的密码和结果的密码进行对比
if($password == $row['password']){
echo "login-pass";
// 失效的访问控制
// url绕过
// 登录成功后,记录session变量
$_SESSION['username'] = $username;
$_SESSION['islogin'] = 'true';
echo " <script>location.href='welcome.php'</script> ";
}
else{
// echo 'password-error'; //不建议直接明确告知用户,是用户还是密码错误,否则对与爆破来说,更容易
// 模糊化,爆破难度增加
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 user where username='$username'and password='$password'";
如果过用户输入 x' or user_id=1#,则申请了语句变成:
$sql= "select * from user where username='x' or user_id=1#'and password='$password'";
如果使用addslashes函数强制为用户输入添加转义符,则变成:
$sql= "select * from user where username='x\' or user_id=1#\'and password='$password'";
上述SQL语句的用户名为: x\'or user_id=1#\',密码为$password,逻辑保持不变
$username = addslashes($_POST['username']);
三、使用mysqli面向对象方式
1、面向过程方式
// 基于面向过程的连接方式
function create_connection() {
// 连接数据库
$conn = mysqli_connect('127.0.0.1','root','root','learn') or die("数据库连接不成功");
// 设置编码格式的两种方法
mysqli_query($conn,"set names utf8");
mysqli_set_charset($conn,'utf8');
return $conn;
};
2、面向对象的方式
// 基于面向对象的连接方式
function create_connection_oop(){
$conn = new mysqli('127.0.0.1','root','root','learn');
// $conn->query("set names utf8");
$conn->set_charset('utf8');
return $conn;
}
// 执行SQL语句
function test_mysli_oop(){
// 连接对象
$conn = create_connection_oop();
$sql = "select * from user where userid < 6";
// 执行$conn对象 query()为方法名 $sql:SQL语句
$result = $conn->query($sql);
// 获取结果集的行数
echo $result->num_rows;
// 获取结果集数组
$rows = $result->fetch_all(MYSQLI_ASSOC);
var_dump($rows);
foreach($rows as $row){
echo "username:".$row['username'].",password:".$row['password']."<br/>";
}
}
test_mysli_oop();
四、使用mysqli预处理功能
1)预处理功能的用法
预处理的过程,就是先交给MySQL数据库进行SQL语句的准备,准备好后再将SQL语句中的参数进行值的替换,
引号会进行转义处理,将所有的参数变成普通字符串,再进行第二次正式的SQL语句执行。
MySQL的预处理即支持面向过程,也支持面向对象的方式,但是我们后续直接使用面向对象的方式。
2)使用预处理来防止SQL注入
// MySQLi预处理功能(面向对象)
function mysqli_prepare_stmt(){
// 先创建数据库的连接
$conn = create_connection_oop();
// SQL语句
$sql = "select * from user where userid < ?"; // ?在预处理语句中用于代替参数
//实例化prepared statemen预处理对象
$stmt = $conn->prepare($sql);
// 实例化后需要将参数值进行绑定并在执行时替换
// bind_param第一个参数为数据类型:i 整数 ,s 字符串, d小数 ,b 二进制
$stmt->bind_param("i",$userid);
$userid = 6;
// 正式执行SQL语句,如果是更新类操作,如update,insert,delete,执行后不做其他操作没有问题
$stmt->execute(); // execute()返回一个布尔类型,表示执行结果成功与否
$conn->commit(); // 默认情况下,更新类操作会自动提交,但是也可以手工处理
// 如果是针对查询语句呢,单纯只是执行无法取得查询结果的,需要进行结果绑定
$sql = "select * from user where userid < ?";
$stmt = $conn->prepare($sql);
$stmt->bind_param("i",$userid);
$userid=6;
// 要获取查询结果,还需要绑定结果参数,就数据库中的列名
$stmt->bind_result($userid, $username,$password,$role);
$stmt->store_result();
// 输出行数
echo $stmt->affected_rows."<br/>";
echo $stmt->num_rows."<br/>";
// 调用结果并进行处理
$stmt->store_result();
while ($stmt->fetch()){
echo $userid,$username,$password,'<br/>';
}
}
3)配置MySQL的临时日志查看SQL语句
在MySQL数据库章运行以下语句,开启临时日志,将日志信息保存到表格MySQL数据库的general——log表中
开启
USE mysql;
SET GLOBAL log_output = "TABLE";
SET GLOBAL general_log = 'ON';
#确认
SHOW VARIABLES LIKE "general_log";
MySQL的预处理功能同样支持面向过程和面向对象。
除了MySQL用于处理数据库外,在PHP中还有最传统MySQL和PDO 两种方式