sql注入漏洞以及PDO防御绕过

一、环境搭建

  1. 安装phpstudy:

    • 下载并安装phpstudy并启动Apache、MySQL等服务。
  2. 配置数据库:

    • 在phpmyadmin创建一个名为test_db的数据库。
      CREATE DATABASE test_db;
      USE test_db;
      
      CREATE TABLE users (
          id INT AUTO_INCREMENT PRIMARY KEY,
          username VARCHAR(50) NOT NULL,
          password VARCHAR(50) NOT NULL
      );
      
      INSERT INTO users (username, password) VALUES ('admin', 'admin123'), ('user1', 'password1');
      
  3. Web页面的配置:

    • 在phpstudy的WWW目录中,创建一个名为sql_injection的文件夹并在该文件夹内创建一个名为login.php的文件,并添加以下代码:
      <?php
      $conn = new mysqli("localhost", "root", "123456", "test_db");
      
      if ($conn->connect_error) {
          die("Connection failed: " . $conn->connect_error);
      }
      
      if ($_SERVER["REQUEST_METHOD"] == "POST") {
          $username = $_POST['username'];
          $password = $_POST['password'];
      
          $sql = "SELECT * FROM users WHERE username='$username' AND password='$password'";
          $result = $conn->query($sql);
      
          if ($result->num_rows > 0) {
              echo "Login successful!";
          } else {
              echo "Invalid credentials!";
          }
      }
      ?>
      
      <form method="POST">
          Username: <input type="text" name="username"><br>
          Password: <input type="password" name="password"><br>
          <input type="submit" value="Login">
      </form>
      
  4. 测试:

    访问http://localhost/sql_injection/login.php,输入用户名和密码进行测试,确保页面工作正常。

二、SQL注入漏洞复现

  1. 常见注入攻击:

    • 在用户名字段中输入:' OR 1=1; --,输入密码123456
    • 点击登录,显示“Login successful!”,则注入成功。
  2. Union查询注入:

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

三、SQL注入防御——PDO

在上面的php代码中,输入的用户名和密码是直接嵌入到SQL查询的字符串中的,所以会引发SQL注入。比如,如果用户输入以下内容作为用户名:

' OR '1'='1

则,生成的SQL查询如下:

SELECT * FROM users WHERE username='' OR '1'='1' AND password='password'

这个SQL查询总是为真,因此攻击者可以绕过登录验证。

解释PDO的防御机制

1. 预处理语句

预处理语句的优势在于,它将 SQL 结构与参数分开,这样即使用户输入了恶意的 SQL 代码,也不会影响原始的 SQL 语句结构。

$sql = "SELECT * FROM users WHERE username = :username";
$stmt = $pdo->prepare($sql);

正如以上,:username 是一个占位符,它表示在执行查询时将由一个实际的值来替代。

2. 参数绑定

参数绑定是指在执行预处理语句时,将用户输入的数据绑定到特定的占位符上。PDO 会自动对输入的数据进行转义,确保其不会被当作 SQL 代码来执行,从而有效防止 SQL 注入。

$username = $_POST['username'];
$stmt->bindParam(':username', $username);
$stmt->execute();

在这段代码中,用户输入的 username 会被绑定到 :username 占位符上。PDO 会确保这个输入值被视作字符串,而不是 SQL 语句的一部分。

防止 SQL 注入

SQL 注入是一种常见的网络攻击方式,攻击者通过将恶意的 SQL 代码插入到输入字段中,试图操纵数据库查询。例如:

如果直接将用户输入嵌入到 SQL 查询中:

$sql = "SELECT * FROM users WHERE username = '" . $_POST['username'] . "'";

如果用户输入 username 为 '; DROP TABLE users; --,则查询将变成:

SELECT * FROM users WHERE username = ''; DROP TABLE users; --';

这可能导致用户表被删除。而使用 PDO 的预处理语句和参数绑定,即使输入了这样的恶意字符串,也不会被执行,因为它仅作为字符串参数被处理。 所以我们可以,

  1. 修改login.php来使用PDO:

    <?php
    try {
        $conn = new PDO("mysql:host=localhost;dbname=test_db", "root", "123456");
        // 设置PDO错误模式为异常模式
        $conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
        echo "Connected successfully<br>";
    } catch(PDOException $e) {
        die("Connection failed: " . $e->getMessage());
    }
    
    if ($_SERVER["REQUEST_METHOD"] == "POST") {
        $username = $_POST['username'];
        $password = $_POST['password'];
    
        // 使用预处理语句防止SQL注入
        $stmt = $conn->prepare("SELECT * FROM users WHERE username = :username AND password = :password");
        $stmt->bindParam(':username', $username);
        $stmt->bindParam(':password', $password);
    
    
        $stmt->execute();
        $result = $stmt->fetchAll();
    
    
        if (count($result) > 0) {
            echo "Login successful!";
        } else {
            echo "Invalid credentials!";
        }
    }
    ?>
    
    <form method="POST">
        Username: <input type="text" name="username"><br>
        Password: <input type="password" name="password"><br>
        <input type="submit" value="Login">
    </form>
  2. 重测:

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

四、绕过PDO

1.错误使用传递

使用了PDO,但是不正确的使用参数绑定传递,二是直接拼接用户的输入,也依旧存在注入风险,就像:

$username = $_POST['username'];
$password = $_POST['password'];

$sql = "SELECT * FROM users WHERE username = '$username' AND password = '$password'";
$stmt = $pdo->query($sql);
$user = $stmt->fetch();

if ($user) {
    echo "Login successful!";
} else {
    echo "Invalid username or password.";
}

$username 和 $password 是用户输入的数据,直接拼接到 SQL 查询字符串中。假设一个攻击者在用户名字段中输入以下内容:

' OR '1'='1

生成的 SQL 查询会变成:

SELECT * FROM users WHERE username = '' OR '1'='1' AND password = '';

OR '1'='1' 是一个总为真的条件,查询将返回数据库中的所有用户记录。即使密码不正确,也可能成功登录。

2. 宽字节注入

假设目前的PHP 应用使用 GBK 编码,且开启了 magic_quotes_gpc(已在 PHP 5.4 中移除)或手动使用了 addslashes() 函数来转义用户输入的单引号 '

$input = "\xdf";  // 用户输入的恶意数据
$escaped_input = addslashes($input);  // 转义后的结果:\xdf

// 最终的 SQL 查询
$query = "SELECT * FROM users WHERE username = '$escaped_input'";
// 结果:SELECT * FROM users WHERE username = '\xdf'

在 GBK 编码下,\xdf 是一个半字符,当与反斜杠 \ 结合在一起时,MySQL 会将其视为一个完整的宽字符,从而忽略了转义。这可能导致单引号 ' 被有效关闭,形成 SQL 注入。

$input = "\xdf' OR '1'='1";  // 用户输入的恶意数据
$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'

这也将导致 SQL 查询返回所有用户记录。

五、总结

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

SQL注入二阶注入(Second-Order Injection)是指攻击者利用已存在的数据库错误或漏洞,对原始的SQL查询进行了动态构造和执行,从而获取到更多的敏感信息或控制权限。这种攻击通常比一级注入更复杂,因为攻击者能够绕过简单的输入验证和转义。 为了防御SQL注入二阶注入,开发者应采取以下措施: 1. **参数化查询/预编译语句**:使用存储过程、ORM(对象关系映射)库或参数化的SQL语句,将数据和命令分开处理,避免直接拼接用户输入字符串。 2. **使用安全的编程语言特性**:如PHP的`mysqli_real_escape_string()`或PDO的参数绑定功能,它们能自动转义特殊字符,防止SQL注入。 3. **最小权限原则**:为应用程序设置数据库连接时,只赋予执行必要操作所需的最低权限,避免攻击者获取更多权限。 4. **输入验证和类型检查**:对用户输入的数据进行严格的验证和格式检查,确保其符合预期的模式。 5. **错误消息防护**:不向用户返回包含敏感信息的错误消息,这可以帮助掩盖系统内部的SQL结构。 6. **定期更新和安全审计**:保持数据库驱动程序和框架的最新版本,并定期审查代码,查找并修复潜在的安全漏洞。 7. **使用Web应用防火墙(WAF)**:一些高级的WAF可以在服务器层面上检测并阻止二阶注入尝试。 8. **限制数据库连接时间**:设置合理的超时机制,防止恶意长时间连接消耗资源。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值