靶场里都用 root 连数据库:账号权限该收到多小
授权与合规声明本文全部操作对象均为自建隔离靶场本机容器或隔离虚拟机涉及安全测试的环节必须以取得合法授权为前提。未经授权的渗透测试违反《中华人民共和国网络安全法》与《刑法》相关条款须承担相应法律责任。本文只讲环境配置、版本对照与靶场隔离不含任何攻击步骤、利用载荷与绕过手法请勿将文中环境指向任何非自有系统。一、现象靶场里那个 root和它带出来的习惯1.1 搭建笔记里最后一行的那个 root打开任何一份靶场搭建笔记连库那一行几乎都是 root口令 root或者干脆留空。这在靶场里没有问题库是刚建的数据是假的跑不起来就删掉重来。root 省掉了为什么这张表看不见为什么这句建表被拒的所有麻烦。问题在于这个习惯会跟着人走。你练完一个注入点回头给自己的小项目写配置多半还是那一行只把地址换成了自己的机器。等到某天想给应用单独开个账号吧才发觉这件事从头到尾没人讲过不知道该给什么不知道给多少算够也不知道给完之后拿什么来确认。1.2 四种听起来像同一件事的现象初学者描述这类问题几乎都用同一句话“权限不对”。但这句话底下至少藏着四件事它们卡在不同的位置属于不同的检查阶段。你看到的现象大概率卡在属于哪个阶段报连不上可用户名口令明明是对的没匹配到你以为的那一行账号记录第一阶段连接验证连上了但看不到目标库里的表库级权限没给或者给的库名不是这个第二阶段库级能看到表建表被拒只有读写权限没有结构变更这一类第二阶段库级或对象级权限全给了还是不能给别的账号授权ALL本身就不含授权权第二阶段授权权四种现象分布在两个阶段、三个层级上名字却是同一个。因为表现都是这句不行最自然的应对就是再给多一点——把 SELECT 加成ALL把shop.*加成*.*。这一招有时候管用不管用的时候你会以为是自己给得还不够。1.3 这篇谈什么不谈什么这篇只谈账号本身的三件事能连到哪来源主机、能对哪个库做什么层级与动词、能不能把权限转手发出去授权权。三件合起来才是该收到多小的完整答案。明确不谈的有四样连接数上限、导出导入、字符集、localhost 与 127.0.0.1 怎么选路。它们都会让人看到连不上但不是权限问题把它们和权限混在一起谈只会让判断更乱。也不谈绕过。这篇从头到尾只回答一个问题一个应用账号该收到多小收完怎么核。任何怎么多拿一点的内容都不在范围内。环境说明先说在前面截至 2026-10-24写这篇用的机器上没有安装mysql与mysqld。所以下面每个 SQL 块都是没跑过的全部标了待验证。能确认的只有没装这一件事。二、权限不是一个开关是按上下文分层的2.1 官方讲权限第一件事是分层手册讲权限体系第一件事不是列清单是分层。它对管理类权限的说法是这些权限是全局的因为它们并不针对某一个特定的库。对库级权限的说法是作用于一个库以及库里所有的对象。对象级还更细一档可以给库里某个具体对象、某类对象也可以给所有库里的所有同类对象。三句话合起来只有一句同一个动词写在不同的ON后面管到的范围完全不同。权限不是有没有的问题是在哪一层有的问题。这一句不先立住后面所有讨论都会滑回给多一点。层级写法管到哪里典型内容全局ON *.*整个实例建库、管账号、管复制、关服务库级ON shop.*一个库及其内部所有对象shop 下所有表的读写表级ON shop.orders一张表只读写这一张列级ON shop.orders (列名)表里的某几列只让看某几列2.2ON *.*与ON shop.*是两个世界这两行只差三个字符权限面差一个量级。差在哪儿全局层有一批没有库可挂的权限。建库、建账号、管复制、关服务这些动作的对象是整个实例所以只能挂在*.*上挂到任何ON shop.*上都是语法错误因为它们不属于那一层。反过来ON shop.*给满了也不会溢出。你在 shop 里能建表能删表不代表能读mysql库不代表能建第二个库也不代表能碰任何实例级的东西。一个很常见的误解是把库级 ALL读成这个库上的最高权限那差不多就是 root 了。不是。库级 ALL 是这个库上能有的全部而 root 站的位置在它上面一整层——差的不是多一点是另一类。这在排障时很关键如果你的问题是看不到另一个库那么在shop.*上怎么加都不可能解决因为你要的东西不在这一层。所以看到ON *.*就当成红灯。教程里写它是因为教程的目标是让环境跑起来你写自己的配置时它绝大多数情况是多余的。2.3 第一个误判把语法能给当成该给因为ALL在每一层都合法语法上你几乎总能写出一句能执行的 GRANT。执行成功会给人一个很强的错觉这件事做对了。但 GRANT 只检查你有没有资格做这个授权动作不检查这个授权合不合理。它不问你为什么给全局权限不问你为什么把来源写成%也不问你为什么给一个只读页面配了删除权限。语法通过和授权合理是两件完全不相干的事。三、三个被读错的词3.1ALL按级别封顶且不含授权权ALL是被误读最多的一个词。它读起来像全部直觉上等于最高。手册的定义是这类写法是某一权限级别上所有可用权限的简写但不包括GRANT OPTION。这半句话里有两层限制。第一层按级别封顶。ALL给的是这一层能有的全部不是这个实例能有的全部。GRANT ALL ON shop.*不会顺带给你建库的权限也不会顺带给你管账号的权限。级别是天花板它只是顶到天花板为止。第二层不含GRANT OPTION。就算写的是GRANT ALL ON *.*拿到的是全局层所有权限但仍然不能发给别人。原文括号里那个 except 就是为这件事写的。所以我给了 ALL还是不能给别的账号授权不是配置出了错是ALL的定义里就写着这一条。很多人在这里怀疑是版本差异或者语法问题实际是没读完那半句。3.2GRANT OPTION不是权限是转发权手册对它的定义是使自己可以把自身拥有的那些权限授予别人或者从别人那里收回。注意那个从句“那些你自己拥有的”。它不能凭空造权只能把手里已有的再分一份。所以在权限模型里它不算一种能力算一种传递关系——它决定这张权限网能不能继续往外长。应用账号要不要判断口径很干脆运行期有没有一条语句是 GRANT 或者 REVOKE没有就不给。给了它等于把账号的权限面从能读写这些表扩到能重新安排谁能读写哪些表。更要紧的是一旦它被用上你在别处做的收权会被绕回去A 收干净了B 还能把权限发回给 A。3.3USAGE它是没有权限不是授权失败USAGE的定义只有一句它代表没有权限。它是占位符账号存在能过第一阶段但在这一层没有任何权限。作用是让该层为空有可写的形式而不是留白。所以SHOW GRANTS里出现USAGE ON *.*非常正常。很多人看到就以为没给上然后继续加一路加到ALL ON *.*才好使——其实真正要给的那条一直在下面只是被第一行挡住了视线。而加到最后恰好就是 2.2 里说的那盏红灯。⚠️代码待验证SHOWGRANTSFORapp10.0.0.5;读这份输出分三步。先数行数一行对应一个层级的一次授权行多通常代表历史遗留多。再看每行是哪个层级ON *.*是全局ON shop.*是库ON shop.orders是表——层级比动词更该先看因为它决定这一行的影响面。最后才看动词拿去和 5.1 的清单对一遍。USAGE行不是错误提示是该层为空的显式写法。判断有没有权限要看完整输出不要停在第一行。四、真正让权限不对的是匹配到的那一行4.1 两阶段先决定能不能连再决定能做什么手册把访问控制分成两个阶段。第一阶段只验一件事这条连接能不能建立判据是身份、凭据以及账号有没有被锁——那条记录里表示锁定的字段必须是 N。第二阶段才逐条语句去验这一句能不能跑。最关键的后果是身份在连接建立的那一刻就钉死了。这条连接之后发的每一条语句都按那个身份查权限不会重新认一次人。这句话能解释两类看着很像、其实不同的问题。“我用 app 连的怎么像是在用另一个账号”——这发生在第一阶段是匹配到了别的行。“我改了权限这条连接还是不行”——这发生在第二阶段和变更对连接的作用时机有关。两类入口完全不同混着看就只能反复重启服务碰运气。4.2 身份不是用户名是用户名加来源主机第一阶段认的身份是User和Host两列共同决定的。手册写得很直白这两列一起决定匹配到哪一行。同一个用户名可以有多行对应不同来源。难的是多行都匹配时用哪一行。规则是服务器按排好序的顺序往下扫用第一个匹配上的行。排序把最具体的 Host 放前面%最不具体。手册还给了主机部分的具体度从 8.0.23 起IP 字面量比 CIDR 具体CIDR 比带掩码的具体%表示任意主机、最不具体空字符串也表示任意主机但排在%之后。行UserHost谁能匹配上具体度1app10.0.0.5只有这一个地址最具体2app10.0.0.0/255.255.255.0这一个网段中间3app%任意来源最不具体这张表的用法不是背排序是意识到一件事你写的行和实际生效的行可能不是同一行。只要同一个用户名下面有多行这个可能就存在。而一旦存在加权限了怎么没用就不需要靠猜了。4.3 空用户名先匹配官方点名的那个误解到这里可以说本文的主线了。手册对空用户名的说明大意是User这一列为空能匹配任何用户名而且一旦匹配上这条连接后续所有的访问检查都用这个空用户名来做。官方在同一节里直接点出这是一个常见的误解——很多人以为连接时显式写了用户名服务器就会先去找那一行。不是。排序在前的行先赢而空用户名的行能匹配任何用户名所以它排在你那一行前面的时候你以为的那个账号压根没被查过。后果很隐蔽你以为在用 app 的权限实际在用匿名行的。给 app 加权限加多少都不生效因为没走到那一行。于是出现一类特别抓狂的现象——SHOW GRANTS FOR app...的输出干净合理应用跑起来还是不行。你会怀疑缓存、怀疑连接池、怀疑配置没生效唯独不怀疑你查的那个账号不是正在用的那个。还有一个容易漏的细节一旦匹配整条连接都用这个身份不是每条语句重新挑一行。判据只有一个手册也写了CURRENT_USER()返回user_namehost_name形式的值取自实际匹配到的那一行记录。⚠️代码待验证SELECTCURRENT_USER();排障第一条命令就是它。别拿连接串里写的用户名当答案——那个是我想要是谁不是我实际是谁。两者的差值就是这类问题的全部空间。⚠️代码待验证SELECTUser,HostFROMmysql.userWHEREUser;这条用来找匿名行需要读mysql库的权限只在你自己有管理权限的靶场里跑。目的只有一个看清楚有没有这样一行、它的 Host 是什么、排在哪。这一步是看清楚不是绕过去。五、收到多小先定库再定动词最后定来源5.1 从这个应用会发什么语句往下减收权的方向不是从最小往上加是从实际会发的语句往下减。差别在心态往上加的人永远在担心加少了会不会出问题往下减的人手里有一份清单减到刚好为止就停。第一步不是开账号是把应用运行期会发哪些语句类型列出来。绝大多数业务应用落在这三档只读的只有查询一般业务是查询加增删改需要改结构的再加建表、改表、删表、建索引。第三档单独说。改结构一般只发生在部署和迁移的时候不在运行期。更稳的做法是给迁移单独开一个账号只在部署时用业务账号永远不碰结构变更。这样一次误操作最多写坏一行数据而不是把整张表连结构一起覆盖掉。需要说明这三档是我的工程建议不是手册原文。手册给的是分层这套定义没有给一个应用该给几个权限的清单因为那取决于应用本身不存在通用答案。这点要说清免得被当官方口径引用。把三档落成表加一列给错的后果权限项运行期要不要给错的后果目标库的查询权限看应用读不读读到了本不该读的表目标库的增、删、改权限看应用写不写写坏或删掉数据目标库的建表、改表、删表一般不要交给迁移账号一次误操作能清掉整张表全局层那一类管理权限不要影响面从这个库扩到整个实例GRANT OPTION不要能把手里权限再发出去mysql库的读权限不要见 5.2来源主机写成%尽量收窄凭据一旦外流可用范围没有边界5.2mysql库不给普通账号读手册里有一条很直接的安全提醒不要给非管理用户读mysql系统库里那些表的权限。这是本文引用的原文里唯一一条直接点名某个库不该给的话值得单独一节。原因是那个库存的不是业务数据是账号和权限本身——有哪些账号、各自从哪些主机来、各自有什么。读到它等于把这张图整份交出去。对防守方来说它是资产对任何拿到这个应用账号的人来说它同样是资产而且是一份能省掉大量猜测的资产。这里有个具体的踩法正好接上 2.2GRANT SELECT ON *.*是包含mysql库的。你本来只想让应用读所有业务库一句*.*就把系统库一起带了进去而且带得毫无声息——它不会提醒你也不会在SHOW GRANTS里单独列一行。5.3 空密码不等于任意密码这一条几乎每个人第一次看到都会理解反。手册的原文大意是authentication_string这一列可以是空的。这不是通配符也不代表任何密码都能匹配。它的意思是这个用户必须在不指定密码的情况下连接。拆成三点。它不是通配符拿任意一个密码去连不会成功所以空密码等于任意密码这个说法是错的。它要求的是不带密码能被用上的条件不是随便填都能过是根本不需要填。所以它的风险不在猜得中在没有这道门槛——前者靠口令强度和限速缓解后者没有强度可言也没有猜的过程。推论很直接一个空密码账号如果 Host 是%那么任何能连到这个端口的人都不需要凭据。这跟口令弱不是一个量级——它压根不是弱是没有。一句校准不要因为它不是任意密码就觉得它安全也不要把它写成任意密码都能进。两头都错一个会漏掉风险一个会让你排障时找错方向。配套资料获取这一章的判断表和应用类型对应只留哪几个动词的对照页我并成了一张单页和正文两张表逐行对应六、自查把你手上的账号过一遍6.1 三步顺序不能换第一步确认匹配到哪一行。SELECT CURRENT_USER();拿回的就是userhost这是判据也是后面所有判断的前提。没有这一步后面两步都建在假设上。第二步看这一行有什么。SHOW GRANTS FOR CURRENT_USER();要用CURRENT_USER()不要硬写用户名——硬写的话你查的是我以为的那个账号而问题往往就出在我以为这三个字上。第三步对着应用真实会发的语句把多出来的砍掉。顺序不能换第一步错了后面两步都是在看别人的权限看得再仔细也没用。而第一步出错恰恰最常见因为它不报错。⚠️代码待验证SELECTCURRENT_USER();SHOWGRANTSFORCURRENT_USER();两句一起跑输出的第一行是实际是谁后面几行是这个身份有什么。拼起来才算一次完整判断。6.2 一张表每一项怎么核把前面几章收敛成一张对照表左列是检查项中列该不该给应用右列怎么看出来。检查项该不该给应用怎么核全局层权限一般不该SHOW GRANTS第一行应当只有USAGE目标库的查询权限按需对一遍应用有没有读操作目标库的增删改权限按需对一遍应用有没有写操作目标库的结构变更权限一般不给交给单独的迁移账号GRANT OPTION不给输出里出现它就算超标mysql库的读权限不给看有没有*.*或mysql.*来源主机收到具体Host是%就要问一句为什么同一用户名的行数越少越好数一下User相同的行有几条空密码账号不该有看authentication_string是否为空6.3 三个常见误判误判一看到USAGE以为没给上。见 3.3出现频率最高因为它看起来就像空。误判二以为ALL是最高。见 3.1最容易被我全给了这句话固化下来之后所有排查都会绕开真正的原因。误判三改了权限以为立刻对所有连接生效。这里必须标待验证——FLUSH PRIVILEGES到底在什么条件下需要本批没有核到对应页面。只能给一句方向性的判断用 GRANT、REVOKE 这类语句改的权限一般不依赖它只有直接去改授权表的时候才可能需要。这句本身也标待验证不要当结论引用。同样没核到的还有一件事权限变更对此前建立的连接什么时候生效。这条在实践里很要命——你在一个会话里改完另一个会话可能还是老样子于是以为没改成功又改一遍。稳妥做法是改完重连一次再验这只是我的建议不是官方结论。七、边界以及收权的顺序7.1 这篇不写什么不写连接数上限那是资源与配置。不写导出导入批量搬数据有自己的权限要求。不写字符集它决定的是存进去的字节怎么解释。不写 localhost 与 127.0.0.1 怎么选路那是连接串与解析层的事和这一行匹配不匹配是两套机制。也不写任何绕过权限的做法不写提权——收权的价值在于把面缩小不在于找到更大的面。whichmysql mysqld本机没有安装mysql与mysqld所以上面所有 SQL 一行都没跑过。这条命令跑过输出是mysql not found与mysqld not found。它证明不了别的只证明我确实没有执行环境这一点得交代否则读者会默认文中的 SQL 都跑通了。7.2 收权顺序先做能立刻看到效果的那一步顺序动作为什么排在这个位置1用CURRENT_USER()确认在用的那一行前面全错的话后面都白做2清理空用户名行和用不到的重复行匹配的不确定性是给多了没用的根源3把来源主机收到具体地址或网段范围越窄凭据外流后的可用面越小4按库收把*.*换成本库.*一步砍掉跨库面和系统库5按动词收去掉结构变更与GRANT OPTION砍掉能改结构和能转发6空密码账号要么删、要么设口令这不是强度问题是有没有门槛7改完重连再验一次变更对连接的作用时机本批未核到顺序不是按投入排的是按错了以后返工代价排的。第 1、2 步几乎零成本但它们决定后面五步是不是在做无用功。第 7 步最容易被省掉而它是唯一能确认改了真的生效的一步。7.3 一句收尾判断该收到多小没有统一答案但有统一的判断方式把这个账号在正常运行中会发的语句列出来只给这些语句需要的权限一个不多。列不出来的部分就是你还不知道应用会做什么的部分那部分先别给。整份都列不出来说明你还不清楚这个应用会做什么——先别开账号。清单是别人的语句是你自己的。配套资料获取这一章的收权七步做成了一张可以照着勾的单页每一步都带做完怎么验和上面的表一一对应附表 A本文引用事实与官方出处对照表编号事实摘要出处本文位置DB-1管理类权限是全局的因其不针对特定库库级权限作用于一个库及其所有对象对象级可细到具体对象或所有库的同类对象MySQL 8.0 Reference Manual 8.2.22.1、2.2DB-2ALL是某一权限级别上所有可用权限的简写且不含GRANT OPTION同上 8.2.23.1DB-3GRANT OPTION使自己可把自身拥有的权限授予或收回给其他用户同上 8.2.23.2DB-4USAGE代表没有权限SHOW GRANTS用它表示该级别无权限同上 8.2.23.3DB-5两阶段访问控制第一阶段只验连接能否建立判据含账号未被锁锁定字段须为 N第二阶段才验语句同上 8.2.64.1DB-6身份由User与Host两列共同决定匹配到哪一行同上 8.2.64.2DB-7服务器按排序后的顺序扫描使用第一个匹配的行最具体的 Host 在前%最不具体同上 8.2.64.2、4.3DB-8User为空可匹配任何用户名匹配后整条连接沿用该空用户名官方点明显式写了用户名就先匹配该用户是常见误解同上 8.2.64.3DB-9CURRENT_USER()返回user_namehost_name取自实际匹配到的那一行记录同上 8.2.64.3、6.1DB-10不要给非管理用户读mysql系统库中表的权限同上 8.2.65.2DB-11authentication_string可为空但不是通配符、不代表任何密码可匹配而是要求不指定密码连接同上 8.2.65.3DB-12主机部分具体度8.0.23 起IP 字面量 CIDR 带掩码%为任意主机且最不具体空串亦为任意主机但排在%之后同上 8.2.64.2DB-13待验证FLUSH PRIVILEGES的触发条件、权限变更对已有连接何时生效本批未核到对应页面6.3、7.2DB-14本机未安装mysql与mysqld全文 SQL 块均为未执行本机实测 2026-10-241.3、7.1 及全文 SQL 块附表 B术语速查表术语一句话说明全局权限挂在ON *.*上、作用于整个实例、不针对某个库的那一类库级权限挂在ON 库名.*上作用于这个库及其内部所有对象ALL“该级别能有的全部”按级别封顶且不含授权权GRANT OPTION转授权能把自己手里的权限再发给别人不是一种权限USAGE代表没有权限的占位写法出现在SHOW GRANTS里属正常SHOW GRANTS按层级逐行列出某账号持有权限的语句一行一层CURRENT_USER()返回实际匹配到的用户名主机是判断我是谁的判据匿名账号User列为空的账号记录可匹配任何用户名来源主机Host列与用户名共同构成身份也决定匹配的先后写在最后这篇用到的资料写这篇文章时我把权限不对拆成两个阶段三个层级重新对了一遍发现真正难的不是给多少而是确认自己到底在用哪一行顺手也整理了几份配套的东西靶场环境对照表DVWA、upload-labs 在 Windows / macOS / Linux 三平台的可行性与推荐路径Web 安全学习路线图从基础打牢到安全管理四个阶段各学什么常用靶场清单每个靶场练什么、适合哪个阶段资料是我自己整理的放在下面这个码上扫码即可获取添加时备注「靶场」优先通过。拿到之后建议先看环境对照表那一份把「我这个应用账号到底连到了哪一行」先对着自己的靶场查一遍比记权限清单更管用。