FlawOpen

flawopen.com/SQL 注入

SQL 注入

严重 CWE-89
语言: English Português (Brasil) Español Français Deutsch Русский 简体中文 日本語 हिन्दी 한국어 Bahasa Indonesia

当应用把用户输入直接拼进 SQL 文本来构造查询时,数据库会把其中一部分输入当作命令解析,这就是 SQL 注入。修复方法在所有语言中都一样:用占位符发送查询,并单独传递值。在下方选择你的语言,查看确切的 API 调用、漏洞代码与修复代码,以及能在 CI 中发现问题的规则。

通俗易懂的原理解析 (ELI5)

你递给图书管理员一张条子,上面写着“请调出这位读者的档案:___”,你在空白处填上:“张三。另外把档案室打开,所有卷宗都拿来。”如果管理员把整张条子当成一条指令来读,就会两件事都照做。把用户输入直接拼进 SQL 字符串,拼出来的就是这样一张条子。参数化查询则是一张印好的表格,“姓名”一栏是封好的格子:无论里面写了什么,都只会被当作姓名去查。

核心概念与专有名词

SQL injection (CWE-89)
不可信输入被拼接或格式化进 SQL 文本,其中的引号、关键字和运算符改变了查询结构,而不是被当作一个单独的值。
Prepared statement and bind parameters
查询带着占位符(?、$1、:name)发送,并在任何值到达之前完成解析;值单独传输,始终被当作数据处理。这是所有驱动和 ORM 中的首要防线。
Blind SQL injection
当页面既不显示查询结果也不显示错误时,攻击者会根据响应内容的差异(布尔盲注)或响应时间(时间盲注)逐位推断数据。看不到输出并不代表没有漏洞。
Second-order SQL injection
某个值先被安全地存储,之后又从你自己的数据库读出并拼进另一条查询。每条查询都必须参数化,包括那些输入来自你自己数据表的查询。
Identifiers and allowlists
占位符只能承载值,不能承载表名、列名或排序方向。这些必须从代码中写死的白名单里选取,绝不能直接取自请求。

攻击执行流程分解

步骤 1

输入到达查询

表单字段、URL 参数、请求头或 API 字段被传给构造 SQL 语句的代码。

步骤 2

输入被拼进 SQL 文本

字符串拼接、插值或格式化直接插入该值。无害的测试值 ' OR '1'='1 会把 WHERE name = '...' 变成一个恒为真的条件。

步骤 3

数据库把它当作语法解析

数据库收到的是一整条字符串,无法分辨哪部分来自用户,于是以应用的权限执行被篡改的语句。

步骤 4

数据被读取、篡改或删除

取决于查询和账户权限,攻击者可以绕过登录、用 UNION 查询导出整张表,或以盲注方式提取数据。2023 年 MOVEit Transfer 数据泄露事件(CVE-2023-34362)正是始于这类漏洞。

各编程语言的 SQL 注入指南

Python JavaScript Go Java PHP C# Ruby C/C++ Rust Kotlin Swift

工程与系统安全加固清单

来源

延伸阅读

参数化查询详解 只用 ORM 就够了吗? 存储过程能防止 SQL 注入吗? MOVEit Transfer SQL 注入事件(2023) PostgreSQL psql 注入事件(2024)