成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

緊急避坑 | MySQL 含有下劃線的數據庫名在特殊情況下導致權限丟失

數據庫 MySQL
當庫名中有多個 "_" 時,情況更為復雜。假設數據庫名稱是 db_1_1,那么授權就不僅是擴大到 38 倍,而是 38 * 38 = 1444 倍,權限擴大的規模超出想象。如果這些庫中有不應該公開的敏感數據,安全性風險將非常嚴重。

在 MySQL 的授權操作中,通配符 "_" 和 "%" 用于匹配單個或多個字符的數據庫對象名。然而,許多 DBA 在進行授權時可能忽視了這些通配符的特殊作用,導致數據庫權限錯配。這篇文章將討論通配符誤用所帶來的潛在風險,并提供避免此類問題的解決方案。

1誤用通配符導致權限授予錯誤

在授權數據庫權限時,如果數據庫名中含有下劃線 _,可能會引發意想不到的結果。我們來看一個常見的授權語句:

GRANT ALL ON `db_1`.* TO  test_user;

表面上看,這個語句似乎是授予用戶 test_user 對數據庫 db_1 的全部權限。然而,通配符 _ 在 MySQL 中具有特殊含義,它用于匹配任意單個字符。因此,這條授權語句實際上可能會匹配多個數據庫,而不僅僅是 db_1。例如,以下數據庫名都可能被匹配:

  • 數據庫名匹配數字:db01,db11,db21,…,db91
  • 數據庫名匹配英文字符:dba1,dbb1
  • 數據庫名匹配特殊字符:db-1,db+1,db?1,等等

這種誤操作可能導致某些用戶意外獲得了不該有的權限,從而帶來嚴重的安全隱患。實際上,按照常見的數據庫命名規范,數據庫名中的字符通常是 26 個英文小寫字母或 10 個數字,也包括 2 種特殊字符(中劃線或下劃線)。因此,這個授權錯誤可能將權限的應用范圍擴大到 38 倍之多。這是基于對命名模式的分析得出的估算,具體情況可能因實際使用的命名規則而有所不同。

2授權帶來的隱患

當庫名中有多個 "_" 時,情況更為復雜。假設數據庫名稱是 db_1_1,那么授權就不僅是擴大到 38 倍,而是 38 * 38 = 1444 倍,權限擴大的規模超出想象。如果這些庫中有不應該公開的敏感數據,安全性風險將非常嚴重。

3如何避免這個問題?

正確的做法:轉義通配符

為了避免這種授權濫用的風險,我們應該將通配符作為普通字符來處理。MySQL 支持使用反斜杠(\)對通配符進行轉義,例如:

GRANT ALL ON `db\_1`.* TO 'test_user';

通過這種方式,_ 將被解釋為字面量,而不是通配符,從而確保授權的僅是特定的 db_1 數據庫。

接下來,文章會多次提到“通配符(_)”和“轉義通配符(\_)”這兩個術語,理解它們的區別有助于避免常見授權錯誤。

阿里云 DMS 等連接工具的優勢

值得注意的是,在使用阿里云 DMS 授權時,系統底層會自動將通配符進行轉義,這也就是為什么很多 DBA 并沒有意識到自己授權時遇到的潛在風險。阿里云的這種機制為用戶省去了手動轉義的煩惱,保證了授權的準確性。

然而,阿里云允許你繞過 DMS,底層手動授權,所以本篇文章內容依然適用于使用阿里云的 DBA。

4整改過程中的風險

在你意識到這個問題后,可能會急于對現有授權進行整改,但需要注意兩種場景:

  • 遺漏整改:部分庫可能沒有徹底整改,仍然使用了通配符授權
  • 保留通配符功能:有些場景下,你希望保留部分通配符授權

在這兩種場景下,會碰到我這篇文章要講的正餐 —— 含有下劃線的數據庫名在特殊情況下會有權限丟失的坑。

5模擬場景:遺漏整改導致權限丟失

現在我們來模擬一個場景,展示如何由于遺漏整改而導致權限問題的發生。

假設在權限整改過程中,你不需要保留通配符的授權,于是你對幾百個數據庫的授權進行了整改,但你還是遺漏了其中一個數據庫,我認為這類情況很有可能發生。該數據庫名為 app_db,其授權如下:

GRANT SELECT, INSERT, UPDATE, DELETE ON `app_db`.* TO `app_user`@`%`;

然后,隨著業務的擴展,你意識到應用程序需要自動維護分區表的能力,因此你希望新增 CREATE、DROP、ALTER

GRANT CREATE, DROP, ALTER ON `app\_db`.* TO `app_user`@`%`;

之后,app_user 的授權狀態如下:

mysql> show grants for app_user;
+----------------------------------------------------------------------+
| Grants for app_user@%                                                |
+----------------------------------------------------------------------+
| GRANT USAGE ON *.* TO `app_user`@`%`                                 |
| GRANT CREATE, DROP, ALTER ON `app\_db`.* TO `app_user`@`%`           |
| GRANT SELECT, INSERT, UPDATE, DELETE ON `app_db`.* TO `app_user`@`%` |
+----------------------------------------------------------------------+
3 rows in set (0.01 sec)

于是,產生了一種,通配符(_)和轉義通配符(\_)混合使用的場景。

表面上看,兩個授權并沒有合并到一條語句,但根據我們前面學到的知識,不難理解,這兩個授權是希望表達:

  • app_user 擁有對 app_db 的 CREATE、DROP、ALTER
  • app_user 也擁有對 app_db 本身及其他符合通配符匹配的數據庫的 SELECT、INSERT、UPDATE、DELETE

表面看似一切正常,但實際上在操作中卻發現了問題。

權限測試

我們來實際測試一下授權效果:

ERROR 1142 (42000): SELECT command denied to user 'app_user'@'127.0.0.1' for table 't'
mysql> insert into `app_db`.t values (1);
ERROR 1142 (42000): INSERT command denied to user 'app_user'@'127.0.0.1' for table 't'
mysql> update `app_db`.t set a=1;
ERROR 1142 (42000): UPDATE command denied to user 'app_user'@'127.0.0.1' for table 't'
mysql> delete from `app_db`.t;
ERROR 1142 (42000): DELETE command denied to user 'app_user'@'127.0.0.1' for table 't'

mysql> create table `app_db`.t2(a int);
Query OK, 0 rows affected (0.01 sec)
mysql> alter table `app_db`.t2 engine=innodb;
Query OK, 0 rows affected (0.02 sec)
Records: 0  Duplicates: 0  Warnings: 0
mysql> drop table `app_db`.t2;
Query OK, 0 rows affected (0.01 sec)

盡管新增的 CREATE、DROP、ALTER 權限生效了,但原來的 SELECT、INSERT、UPDATE 和 DELETE

解釋與分析

這顯然會在生產環境中引發嚴重問題。

那么這是一個 MySQL 的 Bug 嗎?

最初,我也認為這可能是個 S2 級別的 Bug,并向官方提交了報告[1]。

但深入調查后發現,這實際上是 MySQL 授權機制的一個已知行為,而不是 Bug。根據官方文檔[2]:

The use of the wildcard characters % and _ as described in the next few paragraphs is deprecated, and thus subject to removal in a future version of MySQL.

【翻譯】重要提示:接下來幾段中描述的使用通配符 % 和 _ 的方式已被棄用,因此在未來的 MySQL 版本中可能會被移除。

這意味著 MySQL 未來會徹底廢棄通配符在授權中的使用。更進一步的,官方文檔提到:

Issuing multiple GRANT statements containing wildcards may not have the expected effect on DML statements; when resolving grants involving wildcards, MySQL takes only the first matching grant into consideration. In other words, if a user has two database-level grants using wildcards that match the same database, the grant which was created first is applied. Consider the database db and table t created using the statements shown here:

【懶人版翻譯】當多個授權中涉及通配符時,MySQL 只會考慮第一個匹配的授權。

我的案例

我遇到的情況與官方文檔中描述的多個通配符授權略有不同。

官方文檔提到,當涉及多個通配符授權時,MySQL 只會應用第一個匹配的授權,后續的通配符授權將不會生效。然而,在我的案例中,情況有所不同:我只使用了一個通配符授權,之后又添加了一個經過正確轉義的授權。結果是,MySQL 僅識別并應用了轉義后的授權,而原本的通配符授權則被忽略。

這表明,MySQL 在處理通配符和轉義字符時存在文檔不完善的情況。盡管官方文檔中提到通配符授權的局限性,但并未具體說明在混合使用通配符和轉義后的授權時,通配符授權可能會被轉義后的授權所取代。這種情況下,開發者容易誤認為這是一種 Bug,而實際上是 MySQL 授權機制的已知行為。

更進一步測試

上述是基于 MySQL 5.7 測試的結論:

  • 單一授權生效:測試和官方文檔一致,MySQL 只會匹配并生效其中一條授權,不會同時應用兩條授權。—— 這是我的案例里踩到的陷阱。
  • 優先級問題:當通配符授權和轉義通配符授權混合使用時,MySQL 優先應用不含通配符的授權。

然而,在 MySQL 8.0 的測試中,結果又有所不同:哪個授權生效取決于 mysql.db 的加載順序,先進行的授權將優先生效。

為更清楚說明這一問題,我在多個 MySQL 版本中進行了進一步測試,結論如下表所示:

MySQL 版本

混合一個通配符和一個轉義通配符授權的情況,哪個授權最終生效?

5.5

MySQL 會優先使用不含通配符的那個授權

5.7

MySQL 會優先使用不含通配符的那個授權

8.0

先進行的授權將優先生效

8.4

MySQL 會優先使用不含通配符的那個授權

9.0

MySQL 會優先使用不含通配符的那個授權

關于通配符還有一些奇怪的“例外”設置。

In privilege assignments, MySQL interprets occurrences of unescaped _ and % SQL wildcard characters in database names as literal characters under these circumstances:

  • When a database name is not used to grant privileges at the database level, but as a qualifier for granting privileges to some other object such as a table or routine (for example, GRANT ... ON db_name.tbl_name).
  • Enabling partial_revokes causes MySQL to interpret unescaped _ and % wildcard characters in database names as literal characters, just as if they had been escaped as \_ and \%. Because this changes how MySQL interprets privileges, it may be advisable to avoid unescaped wildcard characters in privilege assignments for installations where partial_revokes may be enabled. For more information, see Section 8.2.12, “Privilege Restriction Using Partial Revokes”.

換句話說,如果庫名中的 _ 未轉義,它會被解釋為通配符;但是庫名只是用作表名(表級授權場景)、函數、存儲過程的限定符時,庫名里的 _ 就不再是通配符,而是字面量。

此外,如果你啟用了 MySQL 的部分撤銷授權參數 partial_revokes,數據庫名中的 _ 不需要轉義,它會被直接解釋為字面量。

一會兒是字面量,一會兒又是通配符,難怪官方打算放棄這個功能,連他們自己可能都被搞暈了。通配符的設定確實讓人難以理解

6隱患排查

我們應該和官方一樣,放棄使用通配符授權,使用正確的轉義授權。排查所有使用了 _ 或 % 通配符的情況,統一整改為 \_ 或 \%。

以下 SQL 腳本由 AI 生成,請測試和謹慎使用。

SELECT
    -- 庫名是否含有 _ 或 % 通配符
    CASE
        WHEN EXISTS (
            SELECT 1
            FROM information_schema.schemata
            WHERE INSTR(schema_name, '_') > 0 OR INSTR(schema_name, '%') > 0
        ) THEN '是'
        ELSE '否'
    END AS '庫名是否含有_或%通配符',

    -- 授權里庫名是否使用了 "_" 或 "%" 通配符
    CASE
        WHEN EXISTS (
            SELECT 1
            FROM mysql.db
            WHERE (INSTR(Db, '_') > 0 OR INSTR(Db, '%') > 0)
              AND (INSTR(Db, '\\_') = 0 AND INSTR(Db, '\\%') = 0)
        ) THEN '是'
        ELSE '否'
    END AS '授權里庫名是否使用了"_"或"%"通配符',

    -- 授權里庫名是否使用了 "\_" 或 "\%" 轉義通配符
    CASE
        WHEN EXISTS (
            SELECT 1
            FROM mysql.db
            WHERE INSTR(Db, '\\_') > 0 OR INSTR(Db, '\\%') > 0
        ) THEN '是'
        ELSE '否'
    END AS '授權里庫名是否使用了"\\_"或"\\%"轉義通配符',

    -- 授權里是否存在使用了表級授權的情況(排除指定的兩條記錄)
    CASE
        WHEN EXISTS (
            SELECT 1
            FROM mysql.tables_priv
            WHERE NOT (
                (Host = 'localhost' AND Db = 'mysql' AND User = 'mysql.session' AND Table_name = 'user')
                OR
                (Host = 'localhost' AND Db = 'sys' AND User = 'mysql.sys' AND Table_name = 'sys_config')
            )
        ) THEN '是'
        ELSE '否'
    END AS '授權里是否存在使用了表級授權的情況';

圖片圖片

7如何規避

  • 不要使用通配符授權,多數人不知道這個是通配符,用錯了,要轉義!
  • 不要使用通配符授權,這是官方打算放棄的功能。
  • 如果仍然需要使用通配符授權,不要混合使用,包括不要混合使用轉義通配符授權,不要混合使用多個通配符授權。
  • 如果仍然需要使用通配符授權,仍然打算混合使用,要考慮清楚我文章里的測試結論,測試清楚,例如我的案例里,我可以保留通配符授權情況下,這樣授權。
mysql> SHOW GRANTS FOR app_user;
+-----------------------------------------------------------------------------------------------------+
| Grants for app_user@%                                                                               |
+-----------------------------------------------------------------------------------------------------+
| GRANT USAGE ON *.* TO `app_user`@`%`                                                                |
| GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, DROP, ALTER ON `app\_db`.* TO `app_user`@`%`          |
| GRANT SELECT, INSERT, UPDATE, DELETE ON `app_db`.* TO `app_user`@`%`                                |
+-----------------------------------------------------------------------------------------------------+
3 rows in set (0.01 sec)

參考資料

[1]Bug-116161: https://bugs.mysql.com/bug.php?id=116161

[2]grant: https://dev.mysql.com/doc/refman/8.4/en/grant.html

責任編輯:武曉燕 來源: 愛可生開源社區
相關推薦

2023-11-29 18:11:17

Python代碼

2009-12-01 11:39:39

配置路由器IP

2010-06-30 10:55:13

SQL Server日

2023-11-09 08:55:17

Python雙下劃線

2010-03-04 10:35:51

Python下劃線

2014-05-08 10:36:59

CSS單詞連字符

2022-07-20 08:07:21

數據庫分布式數據庫

2024-08-09 08:28:14

品牌數據庫產品

2011-03-24 11:14:46

2021-08-08 22:27:13

Python下劃線方法

2025-06-05 02:43:00

2011-07-18 09:36:42

Mysql數據庫root@localh

2021-12-26 22:55:26

Python下劃線編程

2020-09-22 09:41:48

Python下劃線開發

2021-07-31 19:21:34

Python下劃線數值

2018-05-17 10:50:34

Cache數據庫存儲

2010-04-14 17:46:10

Oracle數據庫

2018-01-08 19:17:21

數據庫Oracle重啟

2016-12-01 18:57:39

火狐瀏覽器Firefox

2010-08-18 09:45:54

IE6IE7兼容性
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 亚洲福利一区 | 人人天天操 | 精品区一区二区 | 九九综合 | 国产精品a久久久久 | 国产精品久久久久久久三级 | 农夫在线精品视频免费观看 | 午夜精品久久久久久久久久久久久 | 欧美淫 | 91精品国产91综合久久蜜臀 | 国产成人精品久久二区二区91 | 亚洲欧美在线视频 | 日韩欧美视频在线 | 欧美一区二区三区在线 | 国内精品伊人久久久久网站 | 亚洲一视频 | 日本成人三级电影 | 国产欧美一区二区三区在线看 | 日韩精品a在线观看图片 | 伊人色综合久久久天天蜜桃 | 久久久久亚洲国产| 性高湖久久久久久久久3小时 | 黄色欧美大片 | 国产精品视频二区三区 | 日本一区二区三区在线观看 | 成人午夜精品 | 网色 | 国产免费一区二区 | 国产亚洲精品美女久久久久久久久久 | 性高湖久久久久久久久aaaaa | 91久久久久久久久久久 | 99色在线 | 精品国产鲁一鲁一区二区张丽 | 永久免费视频 | 国内久久| 精品在线一区二区三区 | 亚洲高清一区二区三区 | 综合久久99| 成人高清在线视频 | 欧美日韩在线一区二区 | 91在线精品视频 |