### SQL 注入防御方法

1. 使用预编译语句

这是最有效、也是最推荐的防御方法。预编译语句会先将 SQL 语句发送到数据库进行编译，然后将用户输入作为参数传递给编译好的语句。这样一来，用户输入的数据就无法改变 SQL 语句本身的结构

- 原理：将 SQL 语句与用户输入的数据分开处理。数据库会把用户输入的内容看作纯粹的数据，而不是可执行的代码
- 示例：
  - 不安全的代码：`"SELECT * FROM users WHERE username = '" + userInput + "';"`
    - 如果 `userInput` 是 `' OR 1=1 --`，整个语句就变成了 `SELECT * FROM users WHERE username = '' OR 1=1 --;`，从而绕过登录验证
  - 安全的预编译语句：`"SELECT * FROM users WHERE username = ?;"`
    - 这里的问号 `?` 是一个占位符。无论用户输入什么，都会被当作 `username` 字段的值来处理，而不是 SQL 代码

2. 对所有用户输入进行严格验证和过滤

永远不要相信用户的任何输入。在将数据送入数据库之前，必须对其进行验证和过滤

- 白名单验证：只允许特定的字符、格式或值通过。例如，如果某个输入框只接受数字，那么就只允许数字通过
- 黑名单过滤：禁止某些特定的危险字符或字符串，如单引号 `'`、分号 `;`、双破折号 `--` 等。但是，这种方法很容易被绕过，不推荐作为主要的防御手段

3. 使用 ORM 框架

许多现代编程语言的框架都提供了 ORM 工具，例如 Java 的 Hibernate、Python 的 SQLAlchemy、PHP 的 Eloquent。这些框架通常内置了对 SQL 注入的保护机制

- 优点：
  - 安全性：ORM 框架会自动处理参数绑定，将开发者从手动编写安全 SQL 语句的繁琐工作中解放出来
  - 易用性：开发者可以使用面向对象的方式操作数据库，无需直接编写 SQL 语句

4. 最小权限原则

为数据库账户分配最小的权限。一个账户如果只需要读取数据，就只给它 `SELECT` 权限，不要给它 `INSERT`、`UPDATE` 或 `DELETE` 权限

- 好处：即使攻击者成功注入了 SQL 代码，也无法执行超出该账户权限范围的操作，如删除整个数据库

5. 错误信息处理

不要向用户暴露详细的数据库错误信息。攻击者可以利用这些信息来了解数据库结构、版本等，从而更容易发起下一次攻击

- 正确做法：当数据库查询失败时，只向用户显示一个通用的、友好的错误页面，并在后台日志中记录详细信息，供开发者排查

6. 使用 Web 应用防火墙（WAF）

WAF 可以在 Web 应用之前对 HTTP 请求进行过滤和拦截，它可以识别并阻止包含 SQL 注入特征的恶意请求

- 优点：
  - 全面保护：可以为整个应用提供一层额外的保护
  - 实时拦截：在攻击到达应用之前就将其阻止
- 局限性：
  - WAF 的规则可能需要不断更新，以应对新的攻击方式
  - 可能会有误报，影响正常用户的访问