上一篇掌握了 CRUD 增删改查,这一篇进入数据库安全的核心能力------信息获取。作为安全测试者,面对一个陌生的数据库,如何知道它叫什么名字、有哪些表、表里有哪些字段?答案是 MySQL 内置的系统变量和 information_schema 元数据库。掌握这些,即使不知道目标数据库的任何结构,也能通过一条一条查询,把完整的库表字段结构摸清楚。
一、系统变量与函数------获取数据库运行信息
MySQL 内置了一批系统变量和函数,用于返回当前数据库的运行信息。在渗透测试中,这些信息帮助判断数据库版本、当前用户、数据目录等关键情报。
1.1 查看当前数据库
sql
SELECT DATABASE();
SELECT SCHEMA();
返回当前正在使用的数据库名称,用于在多库操作时确认上下文环境。
1.2 查看当前用户
sql
SELECT USER();
SELECT CURRENT_USER();
返回当前登录数据库的用户名和主机地址,用于审计日志和权限验证。
1.3 查看数据库版本
sql
SELECT VERSION();
SELECT @@version;
返回当前 MySQL 服务器的版本号及系统信息。确认版本非常重要,因为不同版本的函数行为和系统表结构存在差异。
1.4 查看服务器环境信息
| 变量 | 作用 |
|---|---|
@@hostname |
运行 MySQL 服务的服务器主机名 |
@@datadir |
数据库核心数据文件的物理存储目录 |
@@basedir |
MySQL 软件程序的安装根目录路径 |
@@plugin_dir |
MySQL 插件扩展功能的默认存放目录 |
@@version_compile_os |
编译当前 MySQL 版本所使用的操作系统 |
@@version_compile_machine |
编译当前 MySQL 版本所使用的硬件架构 |
这些环境变量帮助管理员快速了解系统部署、配置及编译环境,在信息收集中用于判断目标服务器的操作系统和架构。
二、常用字符串函数------信息提取的利器
字符串函数在 SQL 注入信息提取中非常重要,尤其是当页面只能回显一列数据时,需要用拼接函数把多个字段拼到同一个位置一次性输出。
2.1 拼接字符串
sql
-- 基本拼接
SELECT CONCAT('My', 'SQL'); -- 输出 MySQL
-- 拼接表中多个字段
SELECT CONCAT(username, '-', real_name) FROM students;
CONCAT() 是高频函数,当页面只能回显 1 列数据时,就用 CONCAT 把多个字段拼到同一个字段位置,一次性爆出多个字段内容。
2.2 带分隔符拼接
sql
SELECT CONCAT_WS(':', username, phone) FROM students;
CONCAT_WS() 以指定分隔符(这里是冒号)拼接多个字段,把多列数据合并到单个回显位,一次性爆出账号、手机号等多组信息。
2.3 聚合拼接 GROUP_CONCAT
sql
SELECT GROUP_CONCAT(table_name) FROM information_schema.tables
WHERE table_schema = 'school';
GROUP_CONCAT() 是聚合函数,把多行记录拼为一条字符串返回。这是注入中的高频函数------一次性爆出整个库的全部表名,不用多次查询。
2.4 其他常用字符串函数
| 函数 | 作用 | 示例 |
|---|---|---|
LENGTH() |
获取字符串字节长度 | SELECT LENGTH('mysql') → 5 |
CHAR_LENGTH() |
获取字符串字符数 | SELECT CHAR_LENGTH('数据库') → 3 |
SUBSTRING() |
截取子字符串 | SELECT SUBSTRING('database', 1, 4) → data |
LEFT() / RIGHT() |
从左/右截取 | SELECT LEFT('database', 4) → data |
ASCII() / ORD() |
获取字符的 ASCII 码 | SELECT ASCII('A') → 65 |
HEX() |
转十六进制 | SELECT HEX('abc') |
CHAR() |
ASCII 码转字符 | SELECT CHAR(65,66,67) → ABC |
CAST() |
类型转换 | SELECT CAST('123' AS UNSIGNED) |
三、查看库表结构的基础命令
在不知道数据库结构时,第一步是用 MySQL 自带命令快速查看:
sql
-- 查看所有数据库
SHOW DATABASES;
-- 进入指定数据库
USE school;
-- 查看当前库中的所有表
SHOW TABLES;
-- 查看表结构(字段名、类型、约束)
DESC students;
SHOW COLUMNS FROM students;
-- 查看建表完整语句
SHOW CREATE TABLE students;
这些命令是日常管理中最常用的结构查看手段。但在 SQL 注入场景中,很多时候只能通过 SELECT 查询来获取信息,不能直接使用 SHOW/DESC 命令------这就需要用到 information_schema。
四、information_schema------数据库的元数据中心【重点】
4.1 什么是 information_schema
information_schema 是 MySQL 5.7 内置的只读系统数据库,存储所有数据库的元数据(即"数据的数据")。它不是用户创建的,数据库初始化时自动生成,所有 MySQL 5.7 环境默认存在。
它不存储实际业务数据,而是记录了整个数据库服务器的结构信息:有哪些数据库、每个库里有哪些表、每张表里有哪些字段。对于安全测试者来说,它就是数据库的"藏宝图"。
4.2 三张核心元数据表
| 元数据表 | 存储内容 | 核心字段 |
|---|---|---|
schemata |
所有数据库的基本信息 | schema_name(数据库名) |
tables |
所有数据表的详细信息 | table_schema(库名)、table_name(表名) |
columns |
所有字段的结构定义 | table_schema、table_name、column_name(字段名) |
这三张表是数据库枚举的核心。即使一开始完全不知道数据库结构,也可以通过查询这三张表,一步一步把完整结构摸出来。
五、仅通过 SQL 语句枚举完整结构
即使一开始不知道数据库结构,也可以通过系统元数据一步一步把数据库结构查出来。这就是"以查代问"思维------利用元数据探索数据库结构。
5.1 获取所有数据库名
sql
SELECT schema_name FROM information_schema.schemata;
返回服务器上所有数据库名称列表,包括系统库(mysql、information_schema、performance_schema)和业务库(school)。
5.2 获取指定数据库中的所有表名
sql
SELECT table_name FROM information_schema.tables
WHERE table_schema = 'school';
指定 table_schema = 'school',只查 school 库下的表名,返回 classes、students 等表。
注意:查询元数据表时必须通过 WHERE 指定具体数据库,否则会返回所有库的海量信息,导致结果混乱。
5.3 获取指定表中的所有字段名
sql
SELECT column_name
FROM information_schema.columns
WHERE table_schema = 'school'
AND table_name = 'students';
指定库名和表名,返回该表的所有字段名:student_id、username、real_name、gender、age、phone、class_id、created_at。
5.4 获取更完整的字段属性
sql
SELECT
table_schema,
table_name,
column_name,
column_type,
is_nullable,
column_key
FROM information_schema.columns
WHERE table_schema = 'school'
ORDER BY table_name, ordinal_position;
除了字段名,还可以查看字段类型(column_type)、是否允许为空(is_nullable)、是否为主键或索引(column_key)。
5.5 拼接显示完整结构
sql
SELECT
table_name,
GROUP_CONCAT(column_name ORDER BY ordinal_position SEPARATOR ',') AS columns_list
FROM information_schema.columns
WHERE table_schema = 'school'
GROUP BY table_name;
用 GROUP_CONCAT() 把每张表的所有字段名拼成一行,按表名分组显示,一目了然地看到每张表的完整字段列表。
六、枚举逻辑总结
整个数据库枚举遵循"由外而内、层层递进"的逻辑顺序,像剥洋葱一样:
查数据库名(schemata)
↓ 指定 table_schema
查表名(tables)
↓ 指定 table_name
查字段名(columns)
↓ 获取字段属性
查字段类型/约束
| 步骤 | 查询目标 | 数据来源 | 核心条件 |
|---|---|---|---|
| 第一步 | 所有数据库名 | information_schema.schemata | 无 |
| 第二步 | 指定库的所有表名 | information_schema.tables | WHERE table_schema = '库名' |
| 第三步 | 指定表的所有字段名 | information_schema.columns | WHERE table_schema = '库名' AND table_name = '表名' |
| 第四步 | 字段完整属性 | information_schema.columns | 同上,追加 column_type 等 |
合规声明:本课所有 SQL 操作均在本地 phpStudy 的 MySQL 环境中执行,仅用于学习目的。通过 SQL 语句枚举数据库结构属于安全测试知识,仅在授权靶场中练习,严禁对未授权数据库进行任何操作。
七、知识小结
| 知识模块 | 核心内容 | 一句话速记 |
|---|---|---|
| 系统变量 | DATABASE()/USER()/VERSION()/@@version | 获取运行环境信息 |
| 环境变量 | @@hostname/@@datadir/@@basedir 等 | 服务器部署信息 |
| CONCAT | 拼接多个字段到一列 | 回显位不够时用它 |
| GROUP_CONCAT | 聚合多行拼成一条字符串 | 一次爆出全部表名 |
| 字符串函数 | LENGTH/SUBSTRING/ASCII/HEX/CAST | 信息提取与类型转换 |
| information_schema | MySQL 内置只读元数据库 | 数据库的"藏宝图" |
| schemata | 存储所有数据库名 | 第一步查库 |
| tables | 存储所有表名 | 第二步查表 |
| columns | 存储所有字段名 | 第三步查字段 |
| 枚举逻辑 | 库名→表名→字段名→字段属性 | 由外而内层层递进 |
从系统变量到字符串函数,从 information_schema 三张核心表到层层枚举数据库结构,这一篇掌握的是"信息收集"的能力。后续学习 SQL 注入时,无论是联合查询、报错注入还是盲注,其信息获取的底层逻辑都是这些查询语句。记住:查询(DQL)能力决定了后续信息获取的能力,而 information_schema 就是打开数据库结构大门的钥匙。