SELECT和FILE权限必须同时授予,缺一不可;前者用于读取源表数据,后者用于写入服务器磁盘,且FILE权限仅能全局授予(.),必须配合secure_file_priv指定目录才安全。

SELECT 和 FILE 权限必须同时存在,缺一不可 —— 单有 SELECT 无法导出文件,单有 FILE 则连数据都读不到。
为什么只授 SELECT 不够?
很多人以为「能查就能导」,但 SELECT INTO OUTFILE 是个特殊语法:它要求用户既要有源表的 SELECT 权限(读数据),又要有全局 FILE 权限(写服务器磁盘)。这两个权限不互通、不继承,也不能限定到库或表级:FILE 只能用 *.* 授予,且一旦开启,用户就能往服务器任意可写路径写文件(比如 /tmp/ 或 /var/lib/mysql-files/)。
FILE 权限必须搭配 secure_file_priv 配置
secure_file_priv 是 MySQL 的安全闸门,控制 SELECT INTO OUTFILE 和 LOAD DATA INFILE 能写入/读取的目录。它有三种状态:
-
NULL:禁用所有文件导入导出(SELECT INTO OUTFILE直接报错) - 空字符串(
''):不限制路径(最危险,不建议) - 具体路径(如
/var/lib/mysql-files/):只允许操作该目录下的文件(推荐)
检查当前值:
SHOW VARIABLES LIKE 'secure_file_priv';
如果返回是
NULL,需在 my.cnf 中显式配置:secure_file_priv = /var/lib/mysql-files/
改完必须重启 MySQL 生效,且该目录需由
mysql 用户可写。
建账号时必须显式禁用危险权限
即使你只打算给 SELECT + FILE,也要主动检查并清除其他高危权限,因为:
-
GRANT OPTION允许用户再授予权限,可能绕过管控 -
SUPER或PROCESS可能被用来 kill 线程、修改运行时变量 -
SHUTDOWN、RELOAD等管理权限完全没必要给导出账号
标准建号流程示例:
CREATE USER 'exporter'@'192.168.5.%' IDENTIFIED BY 'p4ss_2026';<br>GRANT SELECT ON sales_db.* TO 'exporter'@'192.168.5.%';<br>GRANT FILE ON *.* TO 'exporter'@'192.168.5.%';<br>REVOKE GRANT OPTION, SUPER, SHUTDOWN, RELOAD ON *.* FROM 'exporter'@'192.168.5.%';<br>FLUSH PRIVILEGES;
注意:别漏掉
FLUSH PRIVILEGES,否则新权限不生效;也别用 @'%',host 匹配优先级会让更宽泛的记录覆盖你精心设置的限制。
导出操作本身要注意路径和权限边界
SELECT INTO OUTFILE 写出的文件一定落在 MySQL 服务端,不是你的本地机器。常见误操作:
- 执行
SELECT * FROM t1 INTO OUTFILE '/home/user/export.csv'→ 报错:路径不在secure_file_priv范围内 - 执行
SELECT * FROM t1 INTO OUTFILE '/var/lib/mysql-files/export.csv'→ 成功,但文件在数据库服务器上,需后续scp拉回 - 执行
SELECT * FROM other_db.t2 INTO OUTFILE '/var/lib/mysql-files/t2.csv'→ 报错:没授other_db的SELECT权限,哪怕有FILE
真正安全的导出命令长这样:
SELECT * FROM sales_db.orders WHERE dt = '2026-04-01'<br>INTO OUTFILE '/var/lib/mysql-files/orders_20260401.csv'<br>FIELDS TERMINATED BY ','<br>ENCLOSED BY '"'<br>LINES TERMINATED BY '\n';
字段分隔符、引号包裹、换行符都得显式指定,否则中文或逗号内容会破坏 CSV 结构。
实际部署时最容易被忽略的是 host 匹配逻辑和 secure_file_priv 的联动 —— 它俩一松一紧,就可能让「只读导出账号」意外获得跨库查询能力,或者根本导不出任何东西。


















