sql注入毛病以及PDO防御绕过

瑞星  金牌会员 | 2024-8-29 23:53:35 | 显示全部楼层 | 阅读模式
打印 上一主题 下一主题

主题 852|帖子 852|积分 2556

一、情况搭建


  • 安装phpstudy:

    • 下载并安装phpstudy并启动Apache、MySQL等服务。

  • 配置数据库:

    • 在phpmyadmin创建一个名为test_db的数据库。
      1. CREATE DATABASE test_db;
      2. USE test_db;
      3. CREATE TABLE users (
      4.     id INT AUTO_INCREMENT PRIMARY KEY,
      5.     username VARCHAR(50) NOT NULL,
      6.     password VARCHAR(50) NOT NULL
      7. );
      8. INSERT INTO users (username, password) VALUES ('admin', 'admin123'), ('user1', 'password1');
      复制代码

  • Web页面的配置:

    • 在phpstudy的WWW目录中,创建一个名为sql_injection的文件夹并在该文件夹内创建一个名为login.php的文件,并添加以下代码:
      1. <?php
      2. $conn = new mysqli("localhost", "root", "123456", "test_db");
      3. if ($conn->connect_error) {
      4.     die("Connection failed: " . $conn->connect_error);
      5. }
      6. if ($_SERVER["REQUEST_METHOD"] == "POST") {
      7.     $username = $_POST['username'];
      8.     $password = $_POST['password'];
      9.     $sql = "SELECT * FROM users WHERE username='$username' AND password='$password'";
      10.     $result = $conn->query($sql);
      11.     if ($result->num_rows > 0) {
      12.         echo "Login successful!";
      13.     } else {
      14.         echo "Invalid credentials!";
      15.     }
      16. }
      17. ?>
      18. <form method="POST">
      19.     Username: <input type="text" name="username"><br>
      20.     Password: <input type="password" name="password"><br>
      21.     <input type="submit" value="Login">
      22. </form>
      复制代码

  • 测试:
    访问http://localhost/sql_injection/login.php,输入用户名和暗码举行测试,确保页面工作正常。
二、SQL注入毛病复现


  • 常见注入攻击:

    • 在用户名字段中输入:' OR 1=1; --,输入暗码123456
    • 点击登录,体现“Login successful!”,则注入成功。

  • Union查询注入:

    • 在用户名字段中输入:' UNION SELECT null, table_name FROM information_schema.tables; --,输入暗码123456
    • 输出效果,会看到数据库中的表名。

三、SQL注入防御——PDO

在上面的php代码中,输入的用户名和暗码是直接嵌入到SQL查询的字符串中的,所以会引发SQL注入。比如,如果用户输入以下内容作为用户名:
  1. ' OR '1'='1
复制代码
则,生成的SQL查询如下:
  1. SELECT * FROM users WHERE username='' OR '1'='1
  2. ' AND password='password'
复制代码
这个SQL查询总是为真,因此攻击者可以绕过登录验证。
解释PDO的防御机制


1. 预处理语句

预处理语句的上风在于,它将 SQL 布局与参数分开,这样即利用户输入了恶意的 SQL 代码,也不会影响原始的 SQL 语句布局。
  1. $sql = "SELECT * FROM users WHERE username = :username";
  2. $stmt = $pdo->prepare($sql);
复制代码
正如以上,:username 是一个占位符,它表示在执行查询时将由一个现实的值来替代。
2. 参数绑定

参数绑定是指在执行预处理语句时,将用户输入的数据绑定到特定的占位符上。PDO 会自动对输入的数据举行转义,确保其不会被当作 SQL 代码来执行,从而有效防止 SQL 注入。
  1. $username = $_POST['username'];
  2. $stmt->bindParam(':username', $username);
  3. $stmt->execute();
复制代码
在这段代码中,用户输入的 username 会被绑定到 :username 占位符上。PDO 会确保这个输入值被视作字符串,而不是 SQL 语句的一部分。
防止 SQL 注入

SQL 注入是一种常见的网络攻击方式,攻击者通过将恶意的 SQL 代码插入到输入字段中,试图操纵数据库查询。比方:
如果直接将用户输入嵌入到 SQL 查询中:
  1. $sql = "SELECT * FROM users WHERE username = '" . $_POST['username'] . "'";
复制代码
如果用户输入 username 为 '; DROP TABLE users; --,则查询将酿成:
  1. SELECT * FROM users WHERE username = ''; DROP TABLE users; --';
复制代码
这可能导致用户表被删除。而利用 PDO 的预处理语句和参数绑定,即使输入了这样的恶意字符串,也不会被执行,因为它仅作为字符串参数被处理。 所以我们可以,

  • 修改login.php来利用PDO:
    1. <?php
    2. try {
    3.     $conn = new PDO("mysql:host=localhost;dbname=test_db", "root", "123456");
    4.     // 设置PDO错误模式为异常模式
    5.     $conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
    6.     echo "Connected successfully<br>";
    7. } catch(PDOException $e) {
    8.     die("Connection failed: " . $e->getMessage());
    9. }
    10. if ($_SERVER["REQUEST_METHOD"] == "POST") {
    11.     $username = $_POST['username'];
    12.     $password = $_POST['password'];
    13.     // 使用预处理语句防止SQL注入
    14.     $stmt = $conn->prepare("SELECT * FROM users WHERE username = :username AND password = :password");
    15.     $stmt->bindParam(':username', $username);
    16.     $stmt->bindParam(':password', $password);
    17.     $stmt->execute();
    18.     $result = $stmt->fetchAll();
    19.     if (count($result) > 0) {
    20.         echo "Login successful!";
    21.     } else {
    22.         echo "Invalid credentials!";
    23.     }
    24. }
    25. ?>
    26. <form method="POST">
    27.     Username: <input type="text" name="username"><br>
    28.     Password: <input type="password" name="password"><br>
    29.     <input type="submit" value="Login">
    30. </form>
    复制代码
  • 重测:

    • 重复上述的SQL注入攻击测试,因为用户输入不再直接嵌入SQL语句中,而是通过参数传递,所以防止了SQL注入。

四、绕过PDO

1.错误利用传递

利用了PDO,但是不正确的利用参数绑定传递,二是直接拼接用户的输入,也依旧存在注入风险,就像:
  1. $username = $_POST['username'];
  2. $password = $_POST['password'];
  3. $sql = "SELECT * FROM users WHERE username = '$username' AND password = '$password'";
  4. $stmt = $pdo->query($sql);
  5. $user = $stmt->fetch();
  6. if ($user) {
  7.     echo "Login successful!";
  8. } else {
  9.     echo "Invalid username or password.";
  10. }
复制代码
$username 和 $password 是用户输入的数据,直接拼接到 SQL 查询字符串中。假设一个攻击者在用户名字段中输入以下内容:
  1. ' OR '1'='1
复制代码
生成的 SQL 查询会酿成:
  1. SELECT * FROM users WHERE username = '' OR '1'='1
  2. ' AND password = '';
复制代码
OR '1'='1' 是一个总为真的条件,查询将返回数据库中的所有用户记录。即使暗码不正确,也可能成功登录。
2. 宽字节注入

假设目前的PHP 应用利用 GBK 编码,且开启了 magic_quotes_gpc(已在 PHP 5.4 中移除)或手动利用了 addslashes() 函数来转义用户输入的单引号 '。
  1. $input = "\xdf";  // 用户输入的恶意数据
  2. $escaped_input = addslashes($input);  // 转义后的结果:\xdf
  3. // 最终的 SQL 查询
  4. $query = "SELECT * FROM users WHERE username = '$escaped_input'";
  5. // 结果:SELECT * FROM users WHERE username = '\xdf'
复制代码
在 GBK 编码下,\xdf 是一个半字符,当与反斜杠 \ 联合在一起时,MySQL 会将其视为一个完整的宽字符,从而忽略了转义。这可能导致单引号 ' 被有效关闭,形成 SQL 注入。
  1. $input = "\xdf' OR '1'='1
  2. ";  // 用户输入的恶意数据$escaped_input = addslashes($input);  // 转义后的效果:\xdf\' OR \'1\'=\'1// 最终的 SQL 查询$query = "SELECT * FROM users WHERE username = '$escaped_input'";// 效果:SELECT * FROM users WHERE username = '縗' OR '1'='1
  3. '
复制代码
这也将导致 SQL 查询返回所有用户记录。
五、总结

虽然PDO提供了很强大的防御机制,但条件是正确地利用,包括正确参数绑定,制止直接拼接。还有字符集的指定,及时更新php·························O(∩_∩)O

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

瑞星

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表