據(jù)庫:賬號權限該收到多小)
授權與合規(guī)聲明本文全部操作對象均為自建隔離靶場本機容器或隔離虛擬機涉及安全測試的環(huán)節(jié)必須以取得合法授權為前提。未經授權的滲透測試違反《中華人民共和國網絡安全法》與《刑法》相關條款須承擔相應法律責任。本文只講環(huán)境配置、版本對照與靶場隔離不含任何攻擊步驟、利用載荷與繞過手法請勿將文中環(huán)境指向任何非自有系統(tǒng)。一、現(xiàn)象靶場里那個 root和它帶出來的習慣1.1 搭建筆記里最后一行的那個 root打開任何一份靶場搭建筆記連庫那一行幾乎都是 root口令 root或者干脆留空。這在靶場里沒有問題庫是剛建的數(shù)據(jù)是假的跑不起來就刪掉重來。root 省掉了為什么這張表看不見為什么這句建表被拒的所有麻煩。問題在于這個習慣會跟著人走。你練完一個注入點回頭給自己的小項目寫配置多半還是那一行只把地址換成了自己的機器。等到某天想給應用單獨開個賬號吧才發(fā)覺這件事從頭到尾沒人講過不知道該給什么不知道給多少算夠也不知道給完之后拿什么來確認。1.2 四種聽起來像同一件事的現(xiàn)象初學者描述這類問題幾乎都用同一句話“權限不對”。但這句話底下至少藏著四件事它們卡在不同的位置屬于不同的檢查階段。你看到的現(xiàn)象大概率卡在屬于哪個階段報連不上可用戶名口令明明是對的沒匹配到你以為的那一行賬號記錄第一階段連接驗證連上了但看不到目標庫里的表庫級權限沒給或者給的庫名不是這個第二階段庫級能看到表建表被拒只有讀寫權限沒有結構變更這一類第二階段庫級或對象級權限全給了還是不能給別的賬號授權ALL本身就不含授權權第二階段授權權四種現(xiàn)象分布在兩個階段、三個層級上名字卻是同一個。因為表現(xiàn)都是這句不行最自然的應對就是再給多一點——把 SELECT 加成ALL把shop.*加成*.*。這一招有時候管用不管用的時候你會以為是自己給得還不夠。1.3 這篇談什么不談什么這篇只談賬號本身的三件事能連到哪來源主機、能對哪個庫做什么層級與動詞、能不能把權限轉手發(fā)出去授權權。三件合起來才是該收到多小的完整答案。明確不談的有四樣連接數(shù)上限、導出導入、字符集、localhost 與 127.0.0.1 怎么選路。它們都會讓人看到連不上但不是權限問題把它們和權限混在一起談只會讓判斷更亂。也不談繞過。這篇從頭到尾只回答一個問題一個應用賬號該收到多小收完怎么核。任何怎么多拿一點的內容都不在范圍內。環(huán)境說明先說在前面截至 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 *.*就當成紅燈。教程里寫它是因為教程的目標是讓環(huán)境跑起來你寫自己的配置時它絕大多數(shù)情況是多余的。2.3 第一個誤判把語法能給當成該給因為ALL在每一層都合法語法上你幾乎總能寫出一句能執(zhí)行的 GRANT。執(zhí)行成功會給人一個很強的錯覺這件事做對了。但 GRANT 只檢查你有沒有資格做這個授權動作不檢查這個授權合不合理。它不問你為什么給全局權限不問你為什么把來源寫成%也不問你為什么給一個只讀頁面配了刪除權限。語法通過和授權合理是兩件完全不相干的事。三、三個被讀錯的詞3.1ALL按級別封頂且不含授權權ALL是被誤讀最多的一個詞。它讀起來像全部直覺上等于最高。手冊的定義是這類寫法是某一權限級別上所有可用權限的簡寫但不包括GRANT OPTION。這半句話里有兩層限制。第一層按級別封頂。ALL給的是這一層能有的全部不是這個實例能有的全部。GRANT ALL ON shop.*不會順帶給你建庫的權限也不會順帶給你管賬號的權限。級別是天花板它只是頂?shù)教旎ò鍨橹?。第二層不含GRANT OPTION。就算寫的是GRANT ALL ON *.*拿到的是全局層所有權限但仍然不能發(fā)給別人。原文括號里那個 except 就是為這件事寫的。所以我給了 ALL還是不能給別的賬號授權不是配置出了錯是ALL的定義里就寫著這一條。很多人在這里懷疑是版本差異或者語法問題實際是沒讀完那半句。3.2GRANT OPTION不是權限是轉發(fā)權手冊對它的定義是使自己可以把自身擁有的那些權限授予別人或者從別人那里收回。注意那個從句“那些你自己擁有的”。它不能憑空造權只能把手里已有的再分一份。所以在權限模型里它不算一種能力算一種傳遞關系——它決定這張權限網能不能繼續(xù)往外長。應用賬號要不要判斷口徑很干脆運行期有沒有一條語句是 GRANT 或者 REVOKE沒有就不給。給了它等于把賬號的權限面從能讀寫這些表擴到能重新安排誰能讀寫哪些表。更要緊的是一旦它被用上你在別處做的收權會被繞回去A 收干凈了B 還能把權限發(fā)回給 A。3.3USAGE它是沒有權限不是授權失敗USAGE的定義只有一句它代表沒有權限。它是占位符賬號存在能過第一階段但在這一層沒有任何權限。作用是讓該層為空有可寫的形式而不是留白。所以SHOW GRANTS里出現(xiàn)USAGE ON *.*非常正常。很多人看到就以為沒給上然后繼續(xù)加一路加到ALL ON *.*才好使——其實真正要給的那條一直在下面只是被第一行擋住了視線。而加到最后恰好就是 2.2 里說的那盞紅燈。??代碼待驗證SHOWGRANTSFORapp10.0.0.5;讀這份輸出分三步。先數(shù)行數(shù)一行對應一個層級的一次授權行多通常代表歷史遺留多。再看每行是哪個層級ON *.*是全局ON shop.*是庫ON shop.orders是表——層級比動詞更該先看因為它決定這一行的影響面。最后才看動詞拿去和 5.1 的清單對一遍。USAGE行不是錯誤提示是該層為空的顯式寫法。判斷有沒有權限要看完整輸出不要停在第一行。四、真正讓權限不對的是匹配到的那一行4.1 兩階段先決定能不能連再決定能做什么手冊把訪問控制分成兩個階段。第一階段只驗一件事這條連接能不能建立判據(jù)是身份、憑據(jù)以及賬號有沒有被鎖——那條記錄里表示鎖定的字段必須是 N。第二階段才逐條語句去驗這一句能不能跑。最關鍵的后果是身份在連接建立的那一刻就釘死了。這條連接之后發(fā)的每一條語句都按那個身份查權限不會重新認一次人。這句話能解釋兩類看著很像、其實不同的問題。“我用 app 連的怎么像是在用另一個賬號”——這發(fā)生在第一階段是匹配到了別的行?!拔腋牧藱嘞捱@條連接還是不行”——這發(fā)生在第二階段和變更對連接的作用時機有關。兩類入口完全不同混著看就只能反復重啟服務碰運氣。4.2 身份不是用戶名是用戶名加來源主機第一階段認的身份是User和Host兩列共同決定的。手冊寫得很直白這兩列一起決定匹配到哪一行。同一個用戶名可以有多行對應不同來源。難的是多行都匹配時用哪一行。規(guī)則是服務器按排好序的順序往下掃用第一個匹配上的行。排序把最具體的 Host 放前面%最不具體。手冊還給了主機部分的具體度從 8.0.23 起IP 字面量比 CIDR 具體CIDR 比帶掩碼的具體%表示任意主機、最不具體空字符串也表示任意主機但排在%之后。行UserHost誰能匹配上具體度1app10.0.0.5只有這一個地址最具體2app10.0.0.0/255.255.255.0這一個網段中間3app%任意來源最不具體這張表的用法不是背排序是意識到一件事你寫的行和實際生效的行可能不是同一行。只要同一個用戶名下面有多行這個可能就存在。而一旦存在加權限了怎么沒用就不需要靠猜了。4.3 空用戶名先匹配官方點名的那個誤解到這里可以說本文的主線了。手冊對空用戶名的說明大意是User這一列為空能匹配任何用戶名而且一旦匹配上這條連接后續(xù)所有的訪問檢查都用這個空用戶名來做。官方在同一節(jié)里直接點出這是一個常見的誤解——很多人以為連接時顯式寫了用戶名服務器就會先去找那一行。不是。排序在前的行先贏而空用戶名的行能匹配任何用戶名所以它排在你那一行前面的時候你以為的那個賬號壓根沒被查過。后果很隱蔽你以為在用 app 的權限實際在用匿名行的。給 app 加權限加多少都不生效因為沒走到那一行。于是出現(xiàn)一類特別抓狂的現(xiàn)象——SHOW GRANTS FOR app...的輸出干凈合理應用跑起來還是不行。你會懷疑緩存、懷疑連接池、懷疑配置沒生效唯獨不懷疑你查的那個賬號不是正在用的那個。還有一個容易漏的細節(jié)一旦匹配整條連接都用這個身份不是每條語句重新挑一行。判據(jù)只有一個手冊也寫了CURRENT_USER()返回user_namehost_name形式的值取自實際匹配到的那一行記錄。??代碼待驗證SELECTCURRENT_USER();排障第一條命令就是它。別拿連接串里寫的用戶名當答案——那個是我想要是誰不是我實際是誰。兩者的差值就是這類問題的全部空間。??代碼待驗證SELECTUser,HostFROMmysql.userWHEREUser;這條用來找匿名行需要讀mysql庫的權限只在你自己有管理權限的靶場里跑。目的只有一個看清楚有沒有這樣一行、它的 Host 是什么、排在哪。這一步是看清楚不是繞過去。五、收到多小先定庫再定動詞最后定來源5.1 從這個應用會發(fā)什么語句往下減收權的方向不是從最小往上加是從實際會發(fā)的語句往下減。差別在心態(tài)往上加的人永遠在擔心加少了會不會出問題往下減的人手里有一份清單減到剛好為止就停。第一步不是開賬號是把應用運行期會發(fā)哪些語句類型列出來。絕大多數(shù)業(yè)務應用落在這三檔只讀的只有查詢一般業(yè)務是查詢加增刪改需要改結構的再加建表、改表、刪表、建索引。第三檔單獨說。改結構一般只發(fā)生在部署和遷移的時候不在運行期。更穩(wěn)的做法是給遷移單獨開一個賬號只在部署時用業(yè)務賬號永遠不碰結構變更。這樣一次誤操作最多寫壞一行數(shù)據(jù)而不是把整張表連結構一起覆蓋掉。需要說明這三檔是我的工程建議不是手冊原文。手冊給的是分層這套定義沒有給一個應用該給幾個權限的清單因為那取決于應用本身不存在通用答案。這點要說清免得被當官方口徑引用。把三檔落成表加一列給錯的后果權限項運行期要不要給錯的后果目標庫的查詢權限看應用讀不讀讀到了本不該讀的表目標庫的增、刪、改權限看應用寫不寫寫壞或刪掉數(shù)據(jù)目標庫的建表、改表、刪表一般不要交給遷移賬號一次誤操作能清掉整張表全局層那一類管理權限不要影響面從這個庫擴到整個實例GRANT OPTION不要能把手里權限再發(fā)出去mysql庫的讀權限不要見 5.2來源主機寫成%盡量收窄憑據(jù)一旦外流可用范圍沒有邊界5.2mysql庫不給普通賬號讀手冊里有一條很直接的安全提醒不要給非管理用戶讀mysql系統(tǒng)庫里那些表的權限。這是本文引用的原文里唯一一條直接點名某個庫不該給的話值得單獨一節(jié)。原因是那個庫存的不是業(yè)務數(shù)據(jù)是賬號和權限本身——有哪些賬號、各自從哪些主機來、各自有什么。讀到它等于把這張圖整份交出去。對防守方來說它是資產對任何拿到這個應用賬號的人來說它同樣是資產而且是一份能省掉大量猜測的資產。這里有個具體的踩法正好接上 2.2GRANT SELECT ON *.*是包含mysql庫的。你本來只想讓應用讀所有業(yè)務庫一句*.*就把系統(tǒng)庫一起帶了進去而且?guī)У煤翢o聲息——它不會提醒你也不會在SHOW GRANTS里單獨列一行。5.3 空密碼不等于任意密碼這一條幾乎每個人第一次看到都會理解反。手冊的原文大意是authentication_string這一列可以是空的。這不是通配符也不代表任何密碼都能匹配。它的意思是這個用戶必須在不指定密碼的情況下連接。拆成三點。它不是通配符拿任意一個密碼去連不會成功所以空密碼等于任意密碼這個說法是錯的。它要求的是不帶密碼能被用上的條件不是隨便填都能過是根本不需要填。所以它的風險不在猜得中在沒有這道門檻——前者靠口令強度和限速緩解后者沒有強度可言也沒有猜的過程。推論很直接一個空密碼賬號如果 Host 是%那么任何能連到這個端口的人都不需要憑據(jù)。這跟口令弱不是一個量級——它壓根不是弱是沒有。一句校準不要因為它不是任意密碼就覺得它安全也不要把它寫成任意密碼都能進。兩頭都錯一個會漏掉風險一個會讓你排障時找錯方向。配套資料獲取這一章的判斷表和應用類型對應只留哪幾個動詞的對照頁我并成了一張單頁和正文兩張表逐行對應六、自查把你手上的賬號過一遍6.1 三步順序不能換第一步確認匹配到哪一行。SELECT CURRENT_USER();拿回的就是userhost這是判據(jù)也是后面所有判斷的前提。沒有這一步后面兩步都建在假設上。第二步看這一行有什么。SHOW GRANTS FOR CURRENT_USER();要用CURRENT_USER()不要硬寫用戶名——硬寫的話你查的是我以為的那個賬號而問題往往就出在我以為這三個字上。第三步對著應用真實會發(fā)的語句把多出來的砍掉。順序不能換第一步錯了后面兩步都是在看別人的權限看得再仔細也沒用。而第一步出錯恰恰最常見因為它不報錯。??代碼待驗證SELECTCURRENT_USER();SHOWGRANTSFORCURRENT_USER();兩句一起跑輸出的第一行是實際是誰后面幾行是這個身份有什么。拼起來才算一次完整判斷。6.2 一張表每一項怎么核把前面幾章收斂成一張對照表左列是檢查項中列該不該給應用右列怎么看出來。檢查項該不該給應用怎么核全局層權限一般不該SHOW GRANTS第一行應當只有USAGE目標庫的查詢權限按需對一遍應用有沒有讀操作目標庫的增刪改權限按需對一遍應用有沒有寫操作目標庫的結構變更權限一般不給交給單獨的遷移賬號GRANT OPTION不給輸出里出現(xiàn)它就算超標mysql庫的讀權限不給看有沒有*.*或mysql.*來源主機收到具體Host是%就要問一句為什么同一用戶名的行數(shù)越少越好數(shù)一下User相同的行有幾條空密碼賬號不該有看authentication_string是否為空6.3 三個常見誤判誤判一看到USAGE以為沒給上。見 3.3出現(xiàn)頻率最高因為它看起來就像空。誤判二以為ALL是最高。見 3.1最容易被我全給了這句話固化下來之后所有排查都會繞開真正的原因。誤判三改了權限以為立刻對所有連接生效。這里必須標待驗證——FLUSH PRIVILEGES到底在什么條件下需要本批沒有核到對應頁面。只能給一句方向性的判斷用 GRANT、REVOKE 這類語句改的權限一般不依賴它只有直接去改授權表的時候才可能需要。這句本身也標待驗證不要當結論引用。同樣沒核到的還有一件事權限變更對此前建立的連接什么時候生效。這條在實踐里很要命——你在一個會話里改完另一個會話可能還是老樣子于是以為沒改成功又改一遍。穩(wěn)妥做法是改完重連一次再驗這只是我的建議不是官方結論。七、邊界以及收權的順序7.1 這篇不寫什么不寫連接數(shù)上限那是資源與配置。不寫導出導入批量搬數(shù)據(jù)有自己的權限要求。不寫字符集它決定的是存進去的字節(jié)怎么解釋。不寫 localhost 與 127.0.0.1 怎么選路那是連接串與解析層的事和這一行匹配不匹配是兩套機制。也不寫任何繞過權限的做法不寫提權——收權的價值在于把面縮小不在于找到更大的面。whichmysql mysqld本機沒有安裝mysql與mysqld所以上面所有 SQL 一行都沒跑過。這條命令跑過輸出是mysql not found與mysqld not found。它證明不了別的只證明我確實沒有執(zhí)行環(huán)境這一點得交代否則讀者會默認文中的 SQL 都跑通了。7.2 收權順序先做能立刻看到效果的那一步順序動作為什么排在這個位置1用CURRENT_USER()確認在用的那一行前面全錯的話后面都白做2清理空用戶名行和用不到的重復行匹配的不確定性是給多了沒用的根源3把來源主機收到具體地址或網段范圍越窄憑據(jù)外流后的可用面越小4按庫收把*.*換成本庫.*一步砍掉跨庫面和系統(tǒng)庫5按動詞收去掉結構變更與GRANT OPTION砍掉能改結構和能轉發(fā)6空密碼賬號要么刪、要么設口令這不是強度問題是有沒有門檻7改完重連再驗一次變更對連接的作用時機本批未核到順序不是按投入排的是按錯了以后返工代價排的。第 1、2 步幾乎零成本但它們決定后面五步是不是在做無用功。第 7 步最容易被省掉而它是唯一能確認改了真的生效的一步。7.3 一句收尾判斷該收到多小沒有統(tǒng)一答案但有統(tǒng)一的判斷方式把這個賬號在正常運行中會發(fā)的語句列出來只給這些語句需要的權限一個不多。列不出來的部分就是你還不知道應用會做什么的部分那部分先別給。整份都列不出來說明你還不清楚這個應用會做什么——先別開賬號。清單是別人的語句是你自己的。配套資料獲取這一章的收權七步做成了一張可以照著勾的單頁每一步都帶做完怎么驗和上面的表一一對應附表 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兩階段訪問控制第一階段只驗連接能否建立判據(jù)含賬號未被鎖鎖定字段須為 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系統(tǒng)庫中表的權限同上 8.2.65.2DB-11authentication_string可為空但不是通配符、不代表任何密碼可匹配而是要求不指定密碼連接同上 8.2.65.3DB-12主機部分具體度8.0.23 起IP 字面量 CIDR 帶掩碼%為任意主機且最不具體空串亦為任意主機但排在%之后同上 8.2.64.2DB-13待驗證FLUSH PRIVILEGES的觸發(fā)條件、權限變更對已有連接何時生效本批未核到對應頁面6.3、7.2DB-14本機未安裝mysql與mysqld全文 SQL 塊均為未執(zhí)行本機實測 2026-10-241.3、7.1 及全文 SQL 塊附表 B術語速查表術語一句話說明全局權限掛在ON *.*上、作用于整個實例、不針對某個庫的那一類庫級權限掛在ON 庫名.*上作用于這個庫及其內部所有對象ALL“該級別能有的全部”按級別封頂且不含授權權GRANT OPTION轉授權能把自己手里的權限再發(fā)給別人不是一種權限USAGE代表沒有權限的占位寫法出現(xiàn)在SHOW GRANTS里屬正常SHOW GRANTS按層級逐行列出某賬號持有權限的語句一行一層CURRENT_USER()返回實際匹配到的用戶名主機是判斷我是誰的判據(jù)匿名賬號User列為空的賬號記錄可匹配任何用戶名來源主機Host列與用戶名共同構成身份也決定匹配的先后寫在最后這篇用到的資料寫這篇文章時我把權限不對拆成兩個階段三個層級重新對了一遍發(fā)現(xiàn)真正難的不是給多少而是確認自己到底在用哪一行順手也整理了幾份配套的東西靶場環(huán)境對照表DVWA、upload-labs 在 Windows / macOS / Linux 三平臺的可行性與推薦路徑Web 安全學習路線圖從基礎打牢到安全管理四個階段各學什么常用靶場清單每個靶場練什么、適合哪個階段資料是我自己整理的放在下面這個碼上掃碼即可獲取添加時備注「靶場」優(yōu)先通過。拿到之后建議先看環(huán)境對照表那一份把「我這個應用賬號到底連到了哪一行」先對著自己的靶場查一遍比記權限清單更管用。