SQL 注入防护
防止注入攻击
SQL 注入防护 是 CoddyKit 上的免费 Cyber Security Academy 课时。 这是第 1 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 Cyber Security Academy 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 Cyber Security Academy 课程共包含 4 节课。
什么是 SQL 注入
当不受信任的用户输入被直接拼接到 SQL 查询中时,就会发生SQL 注入(SQLi)。攻击者将 SQL 语法偷偷插入应用程序原本期望接收普通数据的字段,从而改变查询的含义。
SQL 注入至今仍是危害最严重的 Web 漏洞之一,因为它可能泄露整个数据库、绕过登录验证或破坏数据。
存在漏洞的查询
最典型的错误是字符串拼接。当输入为 tom 时,查询没有问题;但经过精心构造的输入会改写查询逻辑。
- 单引号会提前结束字符串。
- 后面的所有内容都会变成可执行的 SQL。
query = "SELECT * FROM users WHERE name = '" + userInput + "'";
// userInput = tom' OR '1'='1
// becomes: SELECT * FROM users WHERE name = 'tom' OR '1'='1'绕过身份验证
登录表单是攻击者的主要目标。通过注入一个恒真的条件并注释掉其余部分,攻击者无需密码即可登录。
-- 序列会注释掉剩余的子句,因此密码检查会被忽略。
-- attacker enters in the username field:
admin' --
-- resulting query:
SELECT * FROM users WHERE user = 'admin' --' AND pass = '...'参数化查询
主要防御措施是参数化查询(预处理语句)。SQL 结构与数据分开传送,因此输入始终会被视为值,而不是代码。
数据库驱动程序会安全地将 ? 占位符绑定到所提供的值。
-- Python (sqlite3 / psycopg)
cur.execute(
'SELECT * FROM users WHERE name = ? AND pass = ?',
(username, password)
)Java 中的预处理语句
每种主流编程语言都支持参数化。在 Java 中,请使用 PreparedStatement,不要使用 Statement 拼接字符串。
绑定的参数无法脱离自己的位置,因此在这里从结构上杜绝了注入。
PreparedStatement ps = conn.prepareStatement(
"SELECT * FROM users WHERE name = ?");
ps.setString(1, userInput);
ResultSet rs = ps.executeQuery();存储过程
如果存储过程在内部使用参数化输入,就能提供帮助。但请注意:如果存储过程通过拼接构建动态 SQL,它同样容易受到攻击。
- 安全:将参数传递给存储过程。
- 不安全:在存储过程中对拼接的字符串执行
EXEC()。
输入验证与允许列表
验证是很有用的第二层防御。请使用允许列表(只接受已知的安全模式),而不是拒绝列表(试图禁止不安全字符)。
例如,数字 ID 字段应在输入到达查询之前拒绝任何包含非数字内容的输入。
if not user_id.isdigit():
raise ValueError('invalid id')
# only then use the value转义是最后手段
手动转义引号既脆弱又容易出错。不同数据库的转义规则各不相同,边界情况(例如编码技巧和二阶注入)也可能漏网。
请优先使用参数化查询。只有在 ORM 或驱动程序无法对特定标识符进行参数化时,才使用转义。
数据库账户的最小权限
为应用程序数据库用户实施最小权限,以限制成功注入可能造成的损害。
- 只为所需的表授予
SELECT、INSERT和UPDATE权限。 - 绝不要在应用程序中使用超级用户或
root账户。 - 拒绝
DROP、FILE以及管理员权限。
GRANT SELECT, INSERT, UPDATE ON appdb.orders TO 'webapp'@'%';
REVOKE DROP, ALTER ON appdb.* FROM 'webapp'@'%';对象关系映射与查询构建器
现代对象关系映射(ORM)(Hibernate、Sequelize、Django ORM、SQLAlchemy)默认会进行参数化,从而消除了大部分注入风险。
当开发人员改用原始 SQL,或在查询构建器中使用字符串插值时,风险就会重新出现。即使处于原始模式,也始终应将值作为绑定参数传入。
纵深防御
单一控制措施并不足够。请组合使用多层防御:
- 在所有位置使用参数化查询(主要措施)。
- 输入验证和允许列表。
- 采用最小权限的数据库账户。
- 使用 Web 应用防火墙(WAF)捕获已知模式。
- 进行错误处理,绝不泄露 SQL 或堆栈跟踪。
快速检查
检验您对主要防御措施的理解。
回顾
您已经了解 SQL 注入的工作原理以及阻止它的方法:
- SQLi 源于将不受信任的输入拼接到查询中。
- 参数化查询是主要防御措施。
- 添加允许列表验证、最小权限的数据库账户和 WAF。
- 避免手动转义,以及避免在存储过程中使用动态 SQL。
纵深防御可以防止单个错误演变成安全 breach。
常见问题解答
「SQL 注入防护」课时是免费的吗?
是的 — 「SQL 注入防护」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 Cyber Security Academy 课程的其余内容,请升级到 CoddyKit PRO。 Cyber Security Academy 课程共包含 4 节课。
「SQL 注入防护」这节课中我会学到什么?
防止注入攻击 你通过在浏览器中直接运行的动手代码来练习 Cyber Security Academy,全天候 AI 导师会在你学习这节课的过程中回答你的问题。
学习 Cyber Security Academy 需要有经验吗?
无需任何先前经验。CoddyKit 上的 Cyber Security Academy 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 1 节课,共 4 节。
「SQL 注入防护」课时需要多长时间?
大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。
我能在这节 Cyber Security Academy 课中编写并运行代码吗?
能。每节 Cyber Security Academy 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。