03-SQL注入-登录漏洞

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 两种方式

  • 19
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值