基于本文回答

播面 播面

文图音视,全方位拆解八股文
0
评论

如何防止SQL 注入?

知识点图片

防止 SQL 注入(SQL Injection)的核心原则是:永远不要信任用户的输入,并将数据与代码(SQL 指令)分离。

以下是防止 SQL 注入最有效的方法,按重要性排序:

1. 使用预编译语句(Prepared Statements)/ 参数化查询 (最重要)

这是防御 SQL 注入的黄金标准

在传统的 SQL 拼接中,数据库无法区分哪部分是指令,哪部分是数据。而使用预编译语句时,数据库会先编译 SQL 模板,然后再将用户输入作为纯粹的“参数”填入。无论输入中包含什么特殊字符(如 ' OR '1'='1),数据库都只将其视为字符串,而不会执行。

错误示例 (Java - 字符串拼接):

java
// 极度危险!
String query = "SELECT * FROM users WHERE username = '" + username + "'";
statement.executeQuery(query);

正确示例 (Java - PreparedStatement):

java
// 安全
String query = "SELECT * FROM users WHERE username = ?";
PreparedStatement pstmt = connection.prepareStatement(query);
pstmt.setString(1, username); // 数据库会自动处理转义
ResultSet results = pstmt.executeQuery();

正确示例 (Python - 使用 sqlite3/psycopg2):

python
# 错误:使用 f-string 或 format
# cursor.execute(f"SELECT * FROM users WHERE name = '{user_input}'")

# 正确:使用库提供的参数占位符
cursor.execute("SELECT * FROM users WHERE name = %s", (user_input,))

2. 使用 ORM 框架 (Object-Relational Mapping)

现代的 ORM 框架(如 Java 的 Hibernate/MyBatis,Python 的 Django ORM/SQLAlchemy,Node.js 的 Sequelize/TypeORM)在底层通常默认使用参数化查询。

只要你使用框架提供的标准 API 进行查询,通常是安全的。

Django 示例:

python
# 安全:Django 会自动处理参数化
User.objects.filter(username=user_input)

注意: 即使使用 ORM,如果你在 ORM 中使用了“原生 SQL 执行”功能(如 raw()execute_sql())且手动拼接了字符串,依然会导致注入漏洞。

3. 输入验证与过滤 (Input Validation)

在数据到达数据库之前,先在应用层进行检查。

  • 白名单验证 (Whitelisting): 比如,如果用户需要选择排序字段(ORDER BY),不要直接接受用户输入的列名,而是检查输入是否在允许的列表(['id', 'name', 'date'])中。
  • 类型检查: 确保 ID 必须是整数,年龄必须是数字,邮箱必须符合邮箱格式。

4. 最小权限原则 (Least Privilege)

不要使用数据库的管理员账号(如 rootsa)来连接应用程序。

  • 为应用程序创建一个专用的数据库用户。
  • 只赋予该用户执行其功能所需的最小权限(例如,只给 SELECT, INSERT, UPDATE 权限,不给 DROP TABLEGRANT 权限)。
  • 这样即使攻击者成功注入,他们造成的破坏也是有限的(例如无法删除整个数据库)。

5. 正确处理错误信息

不要在前端向用户展示详细的数据库错误信息。

  • 如果 SQL 执行出错,攻击者可以通过错误回显(Error-based Injection)推断出数据库的结构。
  • 应用层应该捕获所有数据库异常,并向用户显示通用的错误提示(如“系统繁忙,请稍后再试”),同时将详细错误记录在服务器日志中。

6. 使用 Web 应用防火墙 (WAF)

WAF 可以作为一道防线,帮助识别和拦截常见的 SQL 注入攻击模式(如 URL 中包含 UNION SELECT 等特征字符)。但这只是辅助手段,不能替代代码层面的修复。

总结:如何检查你的代码?

问自己一个问题:“我的 SQL 语句中是否包含了变量拼接?”

  • 如果是 sql = "SELECT ... " + var -> 不安全
  • 如果是 sql = f"SELECT ... {var}" -> 不安全
  • 如果是 db.execute("SELECT ... ?", var) -> 安全

一句话总结:能用参数化查询(预编译)解决的,就绝对不要用字符串拼接。

00:00
00:00