arrow_back返回现场笔记
WEB SECURITY 已发布 8 Aug 2026

什么是SQL注入及如何防止它?

SQL注入攻击的实际分析、真实例子和能在生产代码中真正有效的具体修复方法。

SQL注入自20世纪90年代末以来就存在,至今仍是网络应用被攻击的最常见方式之一。这个漏洞很简单:攻击者发送的输入被解释为SQL代码而不是普通数据,数据库就会执行它本不应该做的事。

攻击的实际运作方式

设想一个登录表单在PHP中构建查询,如下所示:

$query = "SELECT * FROM users WHERE username = '$username' AND password = '$password'";

如果应用没有清理输入,攻击者在用户名字段输入admin' --。查询会变成:

SELECT * FROM users WHERE username = 'admin' --' AND password = ''

--注释掉了该行的其余部分,所以密码检查永远不会执行。这是一个典型的认证绕过。攻击者也会使用UNION SELECT从其他表中提取数据,或者用分号堆叠查询以运行一些破坏性的操作,比如DROP TABLE users;(如果驱动允许多条语句)。

还有盲SQL注入,应用不直接显示查询结果。攻击者通过时间推理(MySQL中的SLEEP(5))或布尔响应(真假条件下页面表现是否不同)推断信息。一旦发现漏洞参数,sqlmap这样的工具就能自动化这种提取。

为什么字符串拼接是根本问题

所有变种的这类攻击都归结为一点:在同一个字符串中混合代码和数据。数据库无法区分合法值和注入语法,因为它们通过同一个通道到达。转义引号在某些情况下有帮助,但很脆弱——不同的编码、二阶注入(数据存储一次,之后不安全地被重复使用)和驱动程序特定的怪癖都会创建绕过路径。转义是一个补丁,不是修复。

参数化查询才是真正的修复

修复是在驱动程序级别将SQL代码与用户数据分开,使用参数化查询(也叫预编译语句)。数据库先接收查询结构,然后再绑定值,所以用户输入永远无法改变查询的含义。

Python中使用psycopg2

cur.execute("SELECT * FROM users WHERE username = %s AND password = %s", (username, password))

Node.js中使用mysql2

connection.execute('SELECT * FROM users WHERE username = ? AND password = ?', [username, password]);

Java中使用JDBC:

PreparedStatement stmt = conn.prepareStatement("SELECT * FROM users WHERE username = ? AND password = ?");
stmt.setString(1, username);
stmt.setString(2, password);

注意这个模式:占位符(%s?)为数据保留位置,实际值单独传递。没有字符串拼接,不需要手动转义。这对你在正常应用中编写的绝大多数查询都有效。

如果动态表名或列名呢?

参数化处理值但不处理标识符——你不能将表名作为参数绑定。如果你的应用需要动态选择表(很少见,通常是设计缺陷的信号),请针对一个硬编码列表将允许的值列入白名单,而不是直接信任用户输入:

allowed_tables = {'orders', 'invoices', 'customers'}
if table_name not in allowed_tables:
    raise ValueError("Invalid table")

永远不要通过用户输入的字符串格式化来构建标识符名称,即使带有转义也不要。

查询本身之外的分层防御

参数化查询是主要控制,但其他几件事也很重要:

  • 数据库账户的最小权限。 应用的数据库用户不应该有DROPALTER或不相关schema的访问权限。如果注入确实发生,有限的权限会限制破坏。
  • ORM默认有帮助。 Django ORM、SQLAlchemy和Hibernate在你使用它们的标准查询构建方法时都自动参数化查询。当开发者退而求其次使用原始SQL或带字符串插值的.extra()/text()调用时,风险就会重新出现——所以要专门审查这些地方。
  • 输入验证是次要层,不是替代品。 检查邮件字段看起来像邮件地址是良好实践,但它本身并不能阻止注入——攻击者会找到仍能通过松散验证的创意负载。
  • WAF可以捕获已知攻击模式,但它是一个检测层,不是底层代码的修复。

测试你自己的代码是否有这个漏洞

将查询通过静态分析工具运行(Python用Bandit,使用SQL注入规则集的Semgrep)作为CI的一部分。对于手动测试,尝试将单引号(')注入到每个输入字段中,看是否有SQL错误消息在响应中泄露——那通常是查询没有参数化的第一个信号。

如果你想深入了解这个,Korra Studio的Web Security课程涵盖注入、XSS和认证绕过,Databases部分演练查询设计模式以完全避免这类漏洞。

本文由人工智能协助撰写,经 Michal Pilch(CISSP)审核并发布,Korra Studio。

准备好更进一步了吗?

这是来自 Korra Studio 知识库的笔记之一——该平台将每个主题与一对一指导相结合。

免费开始arrow_forward