Bối cảnhTôi đang thực hiện một tác vụ di chuyển dữ liệu, trong đó tôi cần xuất một bảng lớn sang tệp CSV cho một công cụ báo cáo. Tôi đã chạy lệnh tiêu chuẩn SELECT ... INTO OUTFILE, mong đợi kết quả nhanh chóng. Thay vào đó, MySQL đã chặn tôi ngay lập tức với một lỗi bảo mật. Điều này cũng thường xuyên xảy ra khi cố gắng tải dữ liệu hàng loạt bằng lệnh LOAD DATA INFILE.
mysql> SELECT * FROM orders INTO OUTFILE '/tmp/orders_dump.csv';
ERROR 1290 (HY000): The MySQL server is running with the --secure-file-priv option so it cannot execute this statement
Lỗi này là một tính năng bảo mật, không phải là lỗi chương trình. MySQL sử dụng biến secure-file-priv để giới hạn nơi máy chủ có thể đọc hoặc ghi tệp. Điều này ngăn chặn kẻ tấn công khi có được quyền truy cập SQL injection có thể đọc các tệp hệ thống nhạy cảm (như /etc/passwd) hoặc ghi các mã độc vào thư mục gốc của trang web (web root).
Quá trình gỡ lỗi (Debug)Điều đầu tiên cần làm là tìm hiểu chính xác cách máy chủ đang bị hạn chế. Bạn có thể kiểm tra giá trị hiện tại của biến secure_file_priv trực tiếp từ MySQL shell:
mysql> SHOW VARIABLES LIKE "secure_file_priv";
+------------------+-----------------------+
| Variable_name | Value |
+------------------+-----------------------+
| secure_file_priv | /var/lib/mysql-files/ |
+------------------+-----------------------+
1 row in set (0.01 sec)
Giải thích kết quả- Một đường dẫn cụ thể (ví dụ: /var/lib/mysql-files/): Máy chủ sẽ chỉ cho phép các hoạt động nhập/xuất trong thư mục cụ thể này.- NULL: Máy chủ đã vô hiệu hóa tất cả các hoạt động nhập và xuất. Điều này thường là mặc định trong các bản cài đặt MySQL mới hơn.- Giá trị trống (""): Không có hạn chế nào. Điều này không an toàn nhưng cho phép bạn đọc/ghi ở bất cứ đâu mà người dùng mysql có quyền ở cấp độ hệ điều hành (OS).## Giải pháp### Cách 1: Sử dụng thư mục được phép (Khuyên dùng)Nếu biến được đặt thành một đường dẫn cụ thể, cách khắc phục dễ dàng và an toàn nhất là chỉ cần di chuyển tệp của bạn vào đó. Thay vì cố gắng ghi vào /tmp/, hãy sử dụng thư mục an toàn đã được chỉ định.
-- Câu lệnh này sẽ hoạt động nếu secure_file_priv là /var/lib/mysql-files/
SELECT * FROM orders
INTO OUTFILE '/var/lib/mysql-files/orders_dump.csv'
FIELDS TERMINATED BY ','
ENCLOSED BY '"'
LINES TERMINATED BY '\n';
Sau khi xuất dữ liệu, bạn có thể di chuyển tệp từ thư mục đó đến đích mong muốn bằng dòng lệnh.
Cách 2: Thay đổi cấu hình (Khắc phục vĩnh viễn)Nếu bạn cần xuất tệp sang một vị trí khác hoặc nếu giá trị hiện tại là NULL, bạn phải sửa đổi tệp cấu hình MySQL.
Trên Linux (Ubuntu/Debian/CentOS)1. Mở tệp cấu hình MySQL của bạn (thường là my.cnf hoặc mysqld.cnf):
sudo nano /etc/mysql/mysql.conf.d/mysqld.cnf
# hoặc
sudo nano /etc/my.cnf
- Tìm mục
[mysqld]và tìm dòngsecure-file-priv. Nếu nó không tồn tại, hãy thêm nó vào. Để vô hiệu hóa hoàn toàn hạn chế, hãy đặt nó thành một chuỗi trống:
[mysqld]
secure-file-priv = ""
- Lưu tệp và khởi động lại dịch vụ MySQL:
sudo systemctl restart mysql
Trên Windows1. Xác định vị trí tệp my.ini của bạn (thường ở C:\ProgramData\MySQL\MySQL Server X.X\). Lưu ý rằng ProgramData là một thư mục ẩn.
- Tìm dòng
secure-file-privvà thay đổi nó:
secure-file-priv=""
- Khởi động lại dịch vụ MySQL bằng
services.msc.
Cách 3: Xử lý với AppArmor (Dành riêng cho Ubuntu)Ngay cả khi bạn đã đặt secure-file-priv = "", mô-đun bảo mật AppArmor của Ubuntu vẫn có thể chặn hoạt động ghi vào một số thư mục nhất định. Nếu bạn thấy lỗi "Permission denied" mặc dù đã thay đổi cấu hình, bạn cần chỉnh sửa profile AppArmor.
sudo nano /etc/apparmor.d/usr.sbin.mysqld
Thêm các đường dẫn bạn muốn cho phép (ví dụ: /data/):
/data/ r,
/data/** rw,
Sau đó tải lại AppArmor:
sudo systemctl reload apparmor
Các bước xác minhĐể xác nhận việc sửa lỗi đã hoạt động, hãy đăng nhập lại vào MySQL và kiểm tra lại biến:
mysql> SHOW VARIABLES LIKE "secure_file_priv";
+------------------+-------+
| Variable_name | Value |
+------------------+-------+
| secure_file_priv | |
+------------------+-------+
Nếu giá trị trống, hãy thử một lệnh xuất kiểm tra nhỏ:
mysql> SELECT 1 INTO OUTFILE '/tmp/test_write.txt';
Query OK, 1 row affected (0.00 sec)
Nếu tệp được tạo thành công trong /tmp/, vấn đề đã được giải quyết.

