### PHP 在做 SQL 注入防御时有哪些方法

1. 使用预处理语句

这是防御 SQL 注入的首选方法，也是最安全、最推荐的方式。预处理语句将 SQL 代码与数据完全分离，数据库会先编译SQL 模板，然后再将用户数据作为参数绑定进去。这样，无论用户输入什么，数据都只会被当作值来处理，而不会被当作SQL 代码的一部分

- PDO (PHP Data Objects)

      // 创建PDO连接
      $pdo = new PDO("mysql:host=localhost;dbname=testdb", "username", "password");

      // 1. 准备SQL语句模板，使用 ? 占位符
      $stmt = $pdo->prepare("SELECT * FROM users WHERE username = ?");

      // 2. 绑定参数，将用户输入作为值传递
      $stmt->execute([$_POST['username']]);

      // 3. 获取结果
      $user = $stmt->fetch();

  PDO 是现代 PHP 开发中处理数据库连接和查询的标准库，它支持多种数据库，并且提供了强大的预处理功能

- MySQLi

      // 创建MySQLi连接
      $mysqli = new mysqli("localhost", "username", "password", "testdb");

      // 1. 准备SQL语句模板，使用 ? 占位符
      $stmt = $mysqli->prepare("SELECT * FROM users WHERE username = ?");

      // 2. 绑定参数，指定数据类型
      $stmt->bind_param("s", $_POST['username']); // "s" 表示 string

      // 3. 执行查询
      $stmt->execute();

      // 4. 获取结果
      $result = $stmt->get_result();
      $user = $result->fetch_assoc();

  MySQLi 是专为 MySQL 设计的扩展，也支持预处理语句

2. 使用 ORM 框架

如果你在使用像 Laravel、Symfony 或 Yii 这样的现代 PHP 框架，那么ORM（对象关系映射）库（如Eloquent或Doctrine）已经为你处理了预处理语句的复杂性

- Laravel Eloquent 示例

      use App\Models\User;

      // Eloquent会自动使用预处理语句
      $user = User::where('username', $_POST['username'])->first();

  使用 ORM，你不再需要直接编写 SQL 语句，而是通过操作对象来完成数据库交互，这从根本上避免了 SQL 注入的可能性

3. 数据转义

在某些老旧的系统或特殊情况下，如果无法使用预处理语句，你必须对所有用户输入进行转义。转义的目的是将用户输入中的特殊字符（如单引号 `'`、双引号 `"`、反斜杠 `\` 等）进行处理，使它们失去原有的特殊含义，被数据库当作普通字符串来处理

- 使用 `mysqli_real_escape_string()`

      // 在使用前确保已经建立了MySQLi连接
      $username = mysqli_real_escape_string($conn, $_POST['username']);

      // 将转义后的变量拼接到SQL语句中
      $sql = "SELECT * FROM users WHERE username = '$username'";
      $result = mysqli_query($conn, $sql);

  这种方法只在最后一道防线使用，并且不推荐作为主要防御手段，因为它容易被遗漏，并且不同的数据库需要不同的转义函数，增加了开发者的负担

- 注意：绝对不要再使用 `addslashes()`，因为它无法处理所有字符集，容易被绕过。`mysql_escape_string()` 也已经被废弃

4. 最小权限原则

这是一个重要的安全原则，可以降低 SQL 注入成功后的危害

- 不要使用 `root` 用户或拥有所有权限的用户来连接数据库
- 为应用程序创建专用的数据库用户，并只赋予它完成任务所需的最小权限。例如，一个只读操作的脚本，其数据库用户就应该只有 `SELECT` 权限

防御方法 | 优点 | 缺点 | 推荐度
--- | --- | --- | ---
预处理语句 | 最安全、最彻底的防御；将数据与代码分离；性能好。 | 语法相对复杂一些；无法用于某些动态 SQL（如表名、列名）。 | 最高
ORM 框架 | 从根本上杜绝 SQL 注入；开发效率高；代码可读性好。 | 需要学习框架；不适用于简单或无框架项目。 | 非常高
数据转义 | 适用于老旧系统或无框架的项目。 | 容易遗漏；依赖开发者记忆；不彻底。 | 低，仅作补充
最小权限 | 降低攻击后的危害。 | 无法从根本上防御注入。 | 非常高（作为安全原则）