Skip to content

Restrict MySQL file privileges

Severity: mediumApplies to: MySQL 8.4 LTSApplies to: MariaDB 11.x / 12.x
The fix/etc/mysql/mysql.conf.d/mysqld.cnf
[mysqld]
secure_file_priv = NULL
local_infile = 0

secure_file_priv = NULL disables file import and export entirely. That is the setting you want unless you have a specific, current need for it.

Terminal window
sudo systemctl restart mysql

The FILE privilege lets an account read and write files as the mysql OS user:

SELECT LOAD_FILE('/etc/passwd');
SELECT '<?php system($_GET[0]); ?>' INTO OUTFILE '/var/www/html/x.php';

That is a SQL injection turning into file disclosure, and then into a web shell if the database user can write anywhere the web server serves. It is the standard escalation path from “SQL injection” to “server compromise”, and it is why keeping FILE away from applications is on this list.

secure_file_priv is the backstop for when a grant is wrong:

Value Effect
NULL Import and export disabled entirely — what you want
/some/dir Restricted to that directory
(empty) No restriction — anywhere the mysql user can reach

An empty value is the dangerous one, and it was the historical default. Packaged installs generally set it to /var/lib/mysql-files/, which is a reasonable middle ground — but NULL is better if nothing uses it, and on most servers nothing does.

local_infile runs in the direction you don’t expect

Section titled “local_infile runs in the direction you don’t expect”

LOAD DATA LOCAL INFILE reads a file from the client and sends it to the server. So the obvious reading is “a user could load their own files” — which is not much of a risk, since they’re their own files.

The real attack goes the other way, and it’s worth understanding because it changes what you protect:

A malicious or compromised MySQL server can ask a connecting client to send it any file. The protocol lets the server respond to a query with “send me this path”, and a client with local_infile enabled complies. Point a database admin tool at a rogue server and it will hand over /etc/passwd, or the client’s SSH keys, or the application config — from the machine running the client.

That reframes the control: local_infile = 0 on your server closes your server as a participant in that attack. It does not protect your clients connecting elsewhere — that’s a client-side setting. If you run tooling that connects to databases you don’t control, disable local infile in the client too.

This is why the page is medium and not higher: the server-side setting closes a narrow thing. The secure_file_priv half is the part that protects you.