01 注入家族总攻:SQL与命令注入


01 注入家族总攻:SQL 与命令注入

SQL 注入是目前影响最深远的 Web 漏洞之一:攻击者把用户输入拼进查询语句,让“你给的字符串”变成“数据库执行的代码”,从而绕过认证、拖走数据、甚至删掉整张表。命令注入则是同一思路的变体,只是目标从数据库换成了服务器操作系统。防御它们共享一句口诀:数据与代码分开,参数化优先。

第二幕的第一课从最“老牌”也最“经典”的注入讲起。上一幕我们强调输入不可信,这一课就是最典型的后果展示——不可信输入没被隔离,直接被拼进了可执行语句。看懂 SQL 注入,不只是为了防一条 SQL,而是为了建立整个注入家族(命令注入、LDAP 注入、XPath 注入、模板注入)的通用直觉。

一、问题与直觉:字符串拼接为什么危险

假设登录查询是这样拼出来的:

SELECT * FROM users WHERE username = '用户输入' AND password = '用户输入'; ​

注意前后两个单引号把输入包成了字符串常量。麻烦来了:如果输入本身就是 ',这个单引号会提前把 SQL 字符串截断,后面的字面量变成语法,用户输入就“逃逸”进了查询的执行域。攻击者只要输入 ' OR '1'='1,并把密码随便填,整条语句变成:

SELECT * FROM users WHERE username = '' OR '1'='1' AND password = '任意串'; ​

因为 '1'='1' 恒为真,WHERE 条件对整个表成立,查询返回所有用户。很多粗糙的登录代码“发现 ID 非空就当登录成功”,于是攻击者直接以第一个用户身份进门。这就是把“数据”放进“代码”位置的直接后果。

二、核心原理:两个执行域的距离

数据库引擎读到的只是最终那一串字符,它不关心哪个部分是程序员写的、哪个部分是用户输入的。只要你把输入拼进语句,用户输入的每个字符都有机会变成 SQL 语法。这就是“拼接注入”的本质:字符串逃逸,语法接管。

扩展到命令注入:如果服务端把用户输入拼进系统命令,例如 system("ping " + 用户输入),输入若为 127.0.0.1; rm -rf /,分号让后续部分成为新命令。同理,模板注入、XPath 注入、LDAP 注入都是用同一个“数据进城变成代码”的模式攻打不同的解析器。

注入家族按照“目标解析器”来分,谁替你解析这段输入,它就是目标所在层。

02-01-fig01

三、实际影响与变式

被攻破后的能力远不止“绕过登录”。利用 UNION 可以跨表合拼结果返回你想要的列;利用注释符 -- 可以截断语句尾部;利用延时函数可以借响应时间做字符逐位盲注,好处是即使页面不直接回显数据也能一点点把库里的内容导出来。若数据库账户权限太高,还可能调用系统命令——这时注入就从“读数据”升级为“拿服务器”。

OWASP 常年把注入列入最常见漏洞前列并非偶然:它门槛低、危险大、又容易因为一句图省事的字符串拼接卷土重来。这也提示一个防守心态——不要抱着“我们输入是内部用的所以安全”的侥幸,任何拼接进语句的值都要按不可信处理,这在第一幕「输入越界」那节的结论里已经种下。

还有一个常在真实事故里出现的后手:注入不一定立刻被发现,很多拖库在数周后才被第三方监测或外部通报戳穿。给查询加一句显式超时、给异常记结构化日志,能显著缩短“已被注入却不自知”的时间窗,把风险的时间系数压制住。

四、工程实践要点:把数据与代码分开

  • 参数化查询 / 预处理语句是第一道也是主要防线:写法是把语句骨架与参数分离,数据库先编译语句、再把参数当纯值填充,无论参数里有什么都不再被当语法。
# 参数化写法:语句骨架与参数分离 import sqlite3 cur = conn.cursor() cur.execute( "SELECT * FROM users WHERE username = ? AND password = ?", (username, password) # 只当值传入,绝不拼进字符串 ) ​
# 危险写法:字符串拼接, 参数里带单引号即可逃逸 cur.execute("SELECT * FROM users WHERE username = '" + username + "' AND password = '" + password + "'") ​
  • 输入验证兜底:参数化之外的验证要看上下文,用白名单核对类型、长度、枚举范围,而不是黑名单过滤“禁止词”——攻击者很快会用编码绕开黑名单。
  • 最小权限原则:数据库连接账户只给业务需要的最小权限,不连高权限 DBA 账号,即使注入发生也难以删库或执行系统命令。
  • 错误信息不外泄:不要让数据库异常直接回显给用户,防止攻击者借报错猜测表结构(这和我们后面错误处理一节的主题呼应)。

⚠️ 常见坑:很多人以为“我字符串里加了关键词过滤就安全”,于是过滤 select、union。但 SeLeCt 大小写混合、注释分隔、URL 编码、Null 字节等手段都能绕过单词过滤。参数化是从根本断掉这条路的唯一稳妥方案。

五、一次完整的演练

背景:把某搜索接口输入作为动态拼接字段的查询改成参数化。操作:先在本地用 sqlite 建一张 users 表,写一个接受输入的查询函数。结果:先让拼接版本读入 ' OR 1=1 -- ,它会返回整表;再切换成参数化版本,同样输入只返回零行(参数化不改变语义)。解读:同一个载荷,拼接版因引号逃逸改变了查询语法,参数化版把载荷当字面量处理,天然免疫。变式:把目标换成命令执行场景,用 ; ls 观察拼接命令的执行与参数化无关的失效方式,验证“不同解析器、同一原理”的结论。

本节要点回顾

  • 拼接注入的本质是把用户输入放进可执行语句的语法位,引号截断导致词法接管。
  • SQL 注入可绕过认证、拖库、删表,高权限账户下还能执行系统命令。
  • 命令注入、模板注入、XPath 注入、LDAP 注入是同一模式的各解析器变体。
  • 参数化查询把语句骨架与参数分离,是根治注入的主要防线。
  • 白名单校验兜底、最小数据库权限、错误不外泄作为纵深补充。
作者与出处
原作者:灏天文库
来源:平台策划编纂
整理人: 灏天文库整理
由灏天文库平台收录,内容或由平台用户上传,仅供学习交流
发布者: 作者: 灏天文库 转发
评论区 (0)
U