它连续十多年稳居 OWASP 十大 Web 安全风险前列,却至今仍在真实攻击中被反复利用。更反直觉的是:发起一次成功的 SQL 注入,可能只需要在输入框里敲一行字。
一、SQL 是什么
SQL(Structured Query Language,结构化查询语言)是关系型数据库的 "通用语言"。无论后端是 MySQL、PostgreSQL、SQL Server 还是 Oracle,应用要读写数据,最终几乎都要翻译成一条 SQL 语句。
它主要归为四类操作:
表格
| 类别 | 全称 | 作用 | 典型语句 |
|---|---|---|---|
| DQL | 数据查询语言 | 读取数据 | SELECT |
| DML | 数据操作语言 | 增删改数据 | INSERT / UPDATE / DELETE |
| DDL | 数据定义语言 | 定义表结构 | CREATE / ALTER / DROP |
| DCL | 数据控制语言 | 权限管理 | GRANT / REVOKE |
一段最普通的登录校验逻辑,翻译成 SQL 大概是这样: 数据库执行它,如果返回了记录,就说明 "账号密码对上了",允许登录。
这里埋着关键前提:SQL 把代码 和数据 写在同一个字符串里。'alice' 和 'p@ssw0rd' 是数据,SELECT、WHERE、AND 是代码。只要能让数据库分不清哪部分是数据、哪部分是代码,攻击就成立了 ------ 这正是 SQL 注入的立足点。
二、它到底是什么
一句话定义
SQL 注入(SQL Injection,SQLi)是指应用程序把不可信的用户输入直接拼接进 SQL 语句,导致攻击者能够改变语句原本的语义,从而越权读取、篡改数据库内容甚至控制数据库服务器的攻击方式。
注意关键词是拼接。
漏洞的产生过程
假设后端的登录代码长这样(以 PHP 为例,其他语言同理): 这段代码没有任何 "过滤",用户提交什么就原样拼进 SQL。当用户填的是正常账号时没问题;当用户填的是精心构造的字符串时,字符串里携带的 SQL 语法就会被数据库当成代码执行。
用一张图说明 "拼接" 的致命性 那个 -- 在 SQL 中是单行注释符,它后面的内容 ------ 包括整段密码校验 ------ 全部失效。于是查询退化成: 只要数据库里存在 admin 这个用户,就一定会返回记录,登录校验的 if (结果非空) 判断随之通过。攻击者不需要知道密码。
常见类型
SQL 注入按利用手法可分为几类,攻击者通常由易到难逐层试探:
表格
| 类型 | 原理 | 典型特征 |
|---|---|---|
| 联合查询注入(Union-based) | 用 UNION SELECT 把额外数据 "挂" 到正常结果集里回显 | 直接把数据读出来,最直观 |
| 报错注入(Error-based) | 故意触发数据库报错,从错误信息里读取内容 | 页面回显 SQL 报错 |
| 布尔盲注(Boolean-based Blind) | 页面不回显数据、也不报错,但 "有结果 / 无结果" 有差异,靠真假条件逐位猜 | 用 AND 1=1 / AND 1=2 对比 |
| 时间盲注(Time-based Blind) | 页面完全没有差异,靠 SLEEP () 制造延迟来传递信息 | AND IF(...,SLEEP(5),0) |
| 堆叠查询(Stacked Queries) | 用分号在同一请求里执行多条语句,可写入或删除数据 | '; DROP TABLE users;-- |
容易被忽视的一点 SQL 注入不只发生在登录框。凡是用户输入最终被带进 SQL 语句的地方,都可能是入口:
- URL 查询参数:
/product?id=1 - 排序 / 筛选参数:
?sort=price&order=desc - HTTP 请求头:User-Agent、X-Forwarded-For(常被写进日志表)
- 表单隐藏字段、JSON 请求体、Cookie
- 甚至接口本身 ------ 只保护页面、不保护 API,是当前最常见的疏漏
据 OWASP 统计,2021 版十大风险中,注入类问题在受测应用中的平均发生率为 3.37%,最高达 19%,是弱点数量排名第二的风险类别 \^1。
三、经典攻击:一行绕过登录
我们完整走一遍前面提到的登录绕过,把每一步都拆开看。
目标系统:一个最朴素的登录接口,用字符串拼接构造 SQL。
原始语句:
攻击步骤:
- 试探闭合方式。在用户名框输入一个单引号
',观察页面是否报错。 如果报You have an error in your SQL syntax...,说明输入是直接拼接的,且用单引号包裹 ------ 漏洞确认。 如果不报错,尝试双引号、)、'))等其他闭合方式。 - 构造绕过 payload。确认闭合方式后,提交: 实际执行的语句变成:
--之后全部被注释掉,密码条件凭空消失。 - 登录成功。系统返回 admin 的记录,认为自己 "验证通过" 了。
如果不知道管理员用户名,还可以改用永真条件: 拼接后: '1'='1' 恒为真,OR 让整个 WHERE 条件恒为真,查询会返回第一条用户记录 ------ 在很多系统里,第一条记录恰好就是管理员。
为什么这么简单的手法至今有效? 因为它利用的不是某种高深漏洞,而是开发者最基础的疏忽:把用户输入当成了 "可信的代码"。这也解释了为什么安全研究者普遍认为,SQL 注入属于 "脚本小子" 级别就能发动的攻击 ------ 工具(如 sqlmap)已经高度自动化,门槛极低。
顺带一提:如果数据库是 MySQL,-- 后面必须跟一个空格才构成注释;URL 传输时需编码为 --+ 或 --%20。SQL Server 中也可用 -- 或 ;--。
四、危害有多大
一次成功的 SQL 注入,危害可以沿数据 → 权限 → 主机三个层级递进,很少止步于 "看到一条数据"。
表格
| 层级 | 攻击者能做什么 | 后果 |
|---|---|---|
| 数据层 | 拖库读取全部表;篡改、删除数据;伪造记录 | 用户隐私、商业机密、账务数据全量泄露 |
| 认证层 | 绕过登录;越权访问他人账户;提升权限为管理员 | 业务系统被完全接管 |
| 主机层 | 读写服务器文件、执行系统命令(如 MySQL 的 INTO OUTFILE、xp_cmdshell) | 从数据库拿下整台服务器,进而横向渗透内网 |
需要强调的是,最严重的往往不是 "数据被看了",而是数据被偷了还不知道。攻击者通常不会立刻破坏系统,而是潜伏数周乃至数月,缓慢拖取数据并植入后门,受害者长期无法察觉。
现实中的代价是复合的:
- 直接财务损失:应急响应、取证、系统加固、法律诉讼、和解赔偿
- 监管处罚:GDPR、数据安全法、个人信息保护法下的罚单
- 业务中断与客户流失:用户信任一旦崩塌,流失往往不可逆
- 声誉与股价:安全事件披露后股价暴跌是常态
下一节的案例会把这几项代价具体化。
五、经典案例分析
案例一:Heartland Payment Systems(2008,美国)
这是美国历史上规模最大的数据泄露案之一,也是最典型的 "SQL 注入 + 内网潜伏" 组合拳。
- 攻击方:Albert Gonzalez 及其同伙
- 入口:用 SQL 注入攻击暴露在外的 Web 表单,拿下系统落脚点
- 手法:拿到访问权后,他们没有立刻拖库,而是在内网植入嗅探器(sniffer)软件,实时截获经过服务器处理的信用卡交易数据 ------ 数据在被加密之前的瞬间被捕获
- 规模:约 1.3 亿张信用卡和借记卡信息被窃取 \^2\^3
后果:
- Heartland 披露了约 1.45 亿美元的泄露相关支出;据部分统计,总成本接近 2 亿美元 \^2
- 股价在披露后数月内下跌约 80%
- Gonzalez 于 2010 年被判处 20 年联邦监禁,是当时美国计算机犯罪的最长刑期 \^2
值得记住的一点:Heartland 在事发前不久刚刚通过了 PCI DSS 合规认证。合规 ≠ 安全------ 标准当时并未要求对传输中的卡数据加密,攻击者恰好利用了这块空白。这次事件直接推动了美国支付行业走向端到端加密、令牌化和 EMV 芯片卡。
案例二:TalkTalk(2015,英国)
一个 "被遗忘的旧页面" 引发的典型事故。
- 入口:攻击者用自动化工具 sqlmap 探测 TalkTalk 网站,发现三个存在 SQL 注入漏洞的页面
- 根源:这些页面来自 TalkTalk 2009 年收购的 Tiscali,旧系统一直在线,运行着过时版本的 MySQL,且当时已有修复补丁却未打。TalkTalk 甚至不知道自己还有这套资产
- 规模:156,959 名客户的姓名、地址、出生日期、电话、邮箱被访问;其中 15,656 人的银行账户信息、约 2.8 万张卡的部分数据也被波及
后果:
- 英国信息专员办公室(ICO)开出 40 万英镑罚单 ------ICO 当时能开出的最高限额是 50 万英镑,这已是创纪录的处罚 \^4
- TalkTalk 自估损失约 7700 万英镑(另有报道称 4200 万英镑),并流失超过 10 万宽带客户
- ICO 调查还发现,此前已有两次 SQL 攻击尝试未被察觉,本可提前预警
ICO 信息专员 Elizabeth Denham 的评价一针见血:
"TalkTalk 未能实施最基本的网络安全措施,让黑客轻而易举地侵入了系统。黑客行为当然是错误的,但这不能成为企业推卸安全责任的借口。"
案例三:Sony Pictures(2014,美国)
事件:自称 "和平守护者"(Guardians of Peace)的组织发动攻击,通过 SQL 注入等手段入侵索尼影业数据库
- 规模:约 40 GB 敏感数据被窃并公开,包括 4.7 万余名前任及现任员工的社保号码、薪资、医疗记录、绩效考评,以及与索尼合作的影星护照签证信息、大量内部邮件,甚至多部未公映影片 \^5
后果:
- 公司系统瘫痪,员工一度只能用纸笔办公;面临前员工集体诉讼;美国联邦调查局(FBI)介入调查,并于 2014 年 12 月正式指认朝鲜为幕后主使(朝方否认)
三个案例的共同点很清晰:技术漏洞本身并不复杂,真正致命的是资产管理、补丁管理、监控响应等基础安全能力的缺失。SQL 注入只是敲门砖,防御的短板决定了损失的上限。
六、如何防范
防范 SQL 注入有明确的优先级,核心只有一条:让数据和代码分离。
6.1 根本解法:参数化查询 / 预编译语句
这是唯一能从根本上消除 SQL 注入的手段,也是所有安全规范的第一要求。它的原理是:SQL 语句的 "骨架" 先被数据库编译固定,用户输入之后以参数的形式传入,永远只能落在 "数据" 的位置上,无法变成 "代码"。
错误写法(拼接): 正确写法(参数化):
注意:并非所有位置都能用参数占位。表名、列名、排序方向(ASC/DESC)无法参数化,只能通过白名单校验处理:
6.2 纵深防御:多层措施,缺一不可
参数化是主力,但完备的防御体系还需要以下各层配合 ------ 假设某一层被绕过,下一层仍能兜住。
表格
| 措施 | 做法 | 作用点 |
|---|---|---|
| 输入校验 | 对数据类型、长度、格式做严格白名单校验(如 id 必须是纯数字,邮箱必须匹配格式) | 入口收窄,减少可疑输入 |
| 最小权限原则 | 应用连接数据库的账号只授予必需的库、表、操作权限,禁止使用 root/sa 等超级账号 | 即使被注入,能造成的破坏也有限 |
| 禁用危险功能 | 关闭 xp_cmdshell、LOAD_FILE、INTO OUTFILE 等高风险特性 | 阻断从数据库逃逸到主机 |
| 存储过程 | 以正确方式(不拼接、用参数)调用存储过程 | 收敛可执行的 SQL 形态 |
| 错误信息脱敏 | 生产环境关闭详细报错,页面只提示 "系统繁忙",完整错误仅写入服务端日志 | 让报错注入无信息可读 |
| WAF | 部署 Web 应用防火墙,拦截常见注入特征 | 拦截已知攻击模式(只能兜底,不能依赖) |
| 日志与监控 | 记录并告警异常 SQL 模式、异常的查询频率 | 缩短发现时间,及时止损 |
| 定期扫描与代码审计 | 用 SAST/DAST 工具(如 sqlmap、Fortify、CodeQL)常态化检测 | 上线前发现,成本最低 |
6.3 上线前自查清单
把下面的清单贴到代码评审流程里,任何一项为 "否" 就应当拒绝合并:
- ✅ 所有涉及用户输入的 SQL 均使用参数化查询,无任何字符串拼接
- ✅ 表名、列名、排序方向等无法参数化的位置,均已做白名单校验
- ✅ 数据库账号遵循最小权限,非超级账号,且按业务拆分
- ✅ 生产环境不向前端暴露 SQL 报错信息与堆栈
- ✅ 已安装的数据库版本无已知未修复漏洞,补丁策略明确
- ✅ 有自动化扫描(SAST/DAST)纳入 CI/CD 流程
- ✅ 数据库访问日志已接入监控与告警,异常模式可被发现
- ✅ 团队内做过 SQL 注入专项安全培训,新成员上手前完成
结语
SQL 注入并不 "高级",它没有零日漏洞的神秘,没有内存破坏的复杂。它之所以生命力顽强,是因为它击中的是一类贯穿所有 Web 开发的思维习惯:默认用户输入是善意的。
对开发者,记住一件事就够了:永远不要让用户输入变成 SQL 代码,参数化查询是默认选项而不是可选项。
对企业,记住案例里的教训:合规认证不等于安全,旧资产、未打补丁的组件、缺失的监控,才是真正的破绽所在。
一条 ' OR '1'='1' -- 能绕过登录,但拦下它也只需要一次 prepare()。