Table of Contents
This section describes general security issues to be aware of and what you can do to make your MySQL installation more secure against attack or misuse. For information specifically about the access control system that MySQL uses for setting up user accounts and checking database access, see Chapter 3, Postinstallation Setup and Testing.
For answers to some questions that are often asked about MySQL Server security issues, see Appendix A, MySQL 5.7 FAQ: Security.
Anyone using MySQL on a computer connected to the Internet should read this section to avoid the most common security mistakes.
In discussing security, it is necessary to consider fully protecting the entire server host (not just the MySQL server) against all types of applicable attacks: eavesdropping, altering, playback, and denial of service. We do not cover all aspects of availability and fault tolerance here.
MySQL uses security based on Access Control Lists (ACLs) for all connections, queries, and other operations that users can attempt to perform. There is also support for SSL-encrypted connections between MySQL clients and servers. Many of the concepts discussed here are not specific to MySQL at all; the same general ideas apply to almost all applications.
When running MySQL, follow these guidelines:
Do not ever give anyone (except MySQL
root accounts) access to the
user table in the mysql
database! This is critical.
Learn how the MySQL access privilege system works (see
Chapter 4, The MySQL Access Privilege System). Use the
GRANT and
REVOKE statements to control
access to MySQL. Do not grant more privileges than necessary.
Never grant privileges to all hosts.
Checklist:
Try mysql -u root. If you are able to
connect successfully to the server without being asked for
a password, anyone can connect to your MySQL server as the
MySQL root user with full privileges!
Review the MySQL installation instructions, paying
particular attention to the information about setting a
root password. See
Section 3.4, “Securing the Initial MySQL Accounts”.
Use the SHOW GRANTS
statement to check which accounts have access to what.
Then use the REVOKE
statement to remove those privileges that are not
necessary.
Do not store cleartext passwords in your database. If your
computer becomes compromised, the intruder can take the full
list of passwords and use them. Instead, use
SHA2(),
SHA1(),
MD5(), or some other one-way
hashing function and store the hash value.
To prevent password recovery using rainbow tables, do not use these functions on a plain password; instead, choose some string to be used as a salt, and use hash(hash(password)+salt) values.
Do not choose passwords from dictionaries. Special programs exist to break passwords. Even passwords like “xfish98” are very bad. Much better is “duag98” which contains the same word “fish” but typed one key to the left on a standard QWERTY keyboard. Another method is to use a password that is taken from the first characters of each word in a sentence (for example, “Four score and seven years ago” results in a password of “Fsasya”). The password is easy to remember and type, but difficult to guess for someone who does not know the sentence. In this case, you can additionally substitute digits for the number words to obtain the phrase “4 score and 7 years ago”, yielding the password “4sa7ya” which is even more difficult to guess.
Invest in a firewall. This protects you from at least 50% of all types of exploits in any software. Put MySQL behind the firewall or in a demilitarized zone (DMZ).
Checklist:
Try to scan your ports from the Internet using a tool such
as nmap. MySQL uses port 3306 by
default. This port should not be accessible from untrusted
hosts. As a simple way to check whether your MySQL port is
open, try the following command from some remote machine,
where server_host is the host
name or IP address of the host on which your MySQL server
runs:
shell> telnet server_host 3306
If telnet hangs or the connection is refused, the port is blocked, which is how you want it to be. If you get a connection and some garbage characters, the port is open, and should be closed on your firewall or router, unless you really have a good reason to keep it open.
Applications that access MySQL should not trust any data entered by users, and should be written using proper defensive programming techniques. See Section 2.7, “Client Programming Security Guidelines”.
Do not transmit plain (unencrypted) data over the Internet. This information is accessible to everyone who has the time and ability to intercept it and use it for their own purposes. Instead, use an encrypted protocol such as SSL or SSH. MySQL supports internal SSL connections. Another technique is to use SSH port-forwarding to create an encrypted (and compressed) tunnel for the communication.
Learn to use the tcpdump and strings utilities. In most cases, you can check whether MySQL data streams are unencrypted by issuing a command like the following:
shell> tcpdump -l -i eth0 -w - src or dst port 3306 | strings
This works under Linux and should work with small modifications under other systems.
If you do not see cleartext data, this does not always mean that the information actually is encrypted. If you need high security, consult with a security expert.
Passwords occur in several contexts within MySQL. The following sections provide guidelines that enable end users and administrators to keep these passwords secure and avoid exposing them. There is also a discussion of how MySQL uses password hashing internally and of a plugin that you can use to enforce stricter passwords.
MySQL users should use the following guidelines to keep passwords secure.
When you run a client program to connect to the MySQL server, it is inadvisable to specify your password in a way that exposes it to discovery by other users. The methods you can use to specify your password when you run client programs are listed here, along with an assessment of the risks of each method. In short, the safest methods are to have the client program prompt for the password or to specify the password in a properly protected option file.
Use the mysql_config_editor utility,
which enables you to store authentication credentials in an
encrypted login path file named
.mylogin.cnf. The file can be read
later by MySQL client programs to obtain authentication
credentials for connecting to MySQL Server. See
mysql_config_editor — MySQL Configuration Utility.
Use a
-p or
your_pass--password=
option on the command line. For example:
your_pass
shell> mysql -u francis -pfrank db_name
This is convenient but insecure. On some systems, your password becomes visible to system status programs such as ps that may be invoked by other users to display command lines. MySQL clients typically overwrite the command-line password argument with zeros during their initialization sequence. However, there is still a brief interval during which the value is visible. Also, on some systems this overwriting strategy is ineffective and the password remains visible to ps. (SystemV Unix systems and perhaps others are subject to this problem.)
If your operating environment is set up to display your current command in the title bar of your terminal window, the password remains visible as long as the command is running, even if the command has scrolled out of view in the window content area.
Use the -p or --password
option on the command line with no password value specified.
In this case, the client program solicits the password
interactively:
shell> mysql -u francis -p db_name
Enter password: ********
The “*” characters indicate
where you enter your password. The password is not displayed
as you enter it.
It is more secure to enter your password this way than to specify it on the command line because it is not visible to other users. However, this method of entering a password is suitable only for programs that you run interactively. If you want to invoke a client from a script that runs noninteractively, there is no opportunity to enter the password from the keyboard. On some systems, you may even find that the first line of your script is read and interpreted (incorrectly) as your password.
Store your password in an option file. For example, on Unix,
you can list your password in the
[client] section of the
.my.cnf file in your home directory:
[client] password=your_pass
To keep the password safe, the file should not be accessible
to anyone but yourself. To ensure this, set the file access
mode to 400 or 600.
For example:
shell> chmod 600 .my.cnf
To name from the command line a specific option file
containing the password, use the
--defaults-file=
option, where file_namefile_name is the full
path name to the file. For example:
shell> mysql --defaults-file=/home/francis/mysql-opts
Using Option Files, discusses option files in more detail.
Store your password in the MYSQL_PWD
environment variable. See
Environment Variables.
This method of specifying your MySQL password must be
considered extremely insecure and
should not be used. Some versions of ps
include an option to display the environment of running
processes. On some systems, if you set
MYSQL_PWD, your password is exposed to
any other user who runs ps. Even on
systems without such a version of ps, it
is unwise to assume that there are no other methods by which
users can examine process environments.
On Unix, the mysql client writes a record of
executed statements to a history file (see
mysql Logging). By default, this file is named
.mysql_history and is created in your home
directory. Passwords can be written as plain text in SQL
statements such as CREATE USER
and ALTER USER, so if you use
these statements, they are logged in the history file. To keep
this file safe, use a restrictive access mode, the same way as
described earlier for the .my.cnf file.
If your command interpreter is configured to maintain a history,
any file in which the commands are saved will contain MySQL
passwords entered on the command line. For example,
bash uses
~/.bash_history. Any such file should have
a restrictive access mode.
Database administrators should use the following guidelines to keep passwords secure.
MySQL stores passwords for user accounts in the
mysql.user table. Access to this table should
never be granted to any nonadministrative accounts.
Account passwords can be expired so that users must reset them. See Section 5.6, “Password Expiration Policy”, and Section 5.7, “Password Expiration and Sandbox Mode”.
The validate_password plugin can be used to
enforce a policy on acceptable password. See
Section 6.2, “The Password Validation Plugin”.
A user who has access to modify the plugin directory (the value
of the plugin_dir system
variable) or the my.cnf file that specifies
the location of the plugin directory can replace plugins and
modify the capabilities provided by plugins, including
authentication plugins.
Files such as log files to which passwords might be written should be protected. See Section 2.2.3, “Passwords and Logging”.
Passwords can be written as plain text in SQL statements such as
CREATE USER,
GRANT, SET
PASSWORD, and statements that invoke the
PASSWORD() function. If such
statements are logged by the MySQL server as written, passwords
in them become visible to anyone with access to the logs.
In MySQL 5.7, statement logging avoids writing passwords in cleartext for the following statements:
CREATE USER ... IDENTIFIED BY ... ALTER USER ... IDENTIFIED BY ... GRANT ... IDENTIFIED BY ... SET PASSWORD ... SLAVE START ... PASSWORD = ... CREATE SERVER ... OPTIONS(... PASSWORD ...) ALTER SERVER ... OPTIONS(... PASSWORD ...)
Passwords in those statements are rewritten to not appear
literally in statement text written to the general query log,
slow query log, and binary log. Rewriting does not apply to
other statements. In particular,
INSERT or
UPDATE statements for the
mysql.user table that refer to literal
passwords are logged as is, so you should avoid such statements.
(Direct manipulation of grant tables is discouraged, anyway.)
For the general query log, password rewriting can be suppressed
by starting the server with the
--log-raw option. For security
reasons, this option is not recommended for production use. For
diagnostic purposes, it may be useful to see the exact text of
statements as received by the server.
Contents of the audit log file produced by the audit log plugin are not encrypted. For security reasons, this file should be written to a directory accessible only to the MySQL server and users with a legitimate reason to view the log. See Section 6.4.3, “MySQL Enterprise Audit Security Considerations”.
Statements received by the server may be rewritten if a query
rewrite plugin is installed (see
Query Rewrite Plugins). In this case, the
--log-raw option affects
statement logging as follows:
An implication of password rewriting is that statements that
cannot be parsed (due, for example, to syntax errors) are not
written to the general query log because they cannot be known to
be password free. Use cases that require logging of all
statements including those with errors should use the
--log-raw option, bearing in mind
that this also bypasses password rewriting.
Password rewriting occurs only when plain text passwords are expected. For statements with syntax that expect a password hash value, no rewriting occurs. If a plain text password is supplied erroneously for such syntax, the password is logged as given, without rewriting. For example, the following statement is logged as shown because a password hash value is expected:
CREATE USER 'user1'@'localhost' IDENTIFIED BY PASSWORD 'not-so-secret';
To guard log files against unwarranted exposure, locate them in
a directory that restricts access to the server and the database
administrator. If the server logs to tables in the
mysql database, grant access to those tables
only to the database administrator.
Replication slaves store the password for the replication master
in the master info repository, which can be either a file or a
table (see Replication Relay and Status Logs). Ensure that the
repository can be accessed only by the database administrator.
An alternative to storing the password in a file is to use the
START SLAVE statement to specify
credentials for connecting to the master.
Use a restricted access mode to protect database backups that include log tables or log files containing passwords.
The information in this section applies fully only before
MySQL 5.7.5, and only for accounts that use the
mysql_native_password or
mysql_old_password authentication plugins.
Support for pre-4.1 password hashes is removed in MySQL 5.7.5.
This includes removal of the
mysql_old_password authentication plugin
and the OLD_PASSWORD()
function. Also, secure_auth
cannot be disabled, and
old_passwords cannot be set
to 1.
As of MySQL 5.7.5, only the information about 4.1 password
hashes and the mysql_native_password
authentication plugin remains relevant.
MySQL lists user accounts in the user table
of the mysql database. Each MySQL account can
be assigned a password, although the user
table does not store the cleartext version of the password, but
a hash value computed from it.
MySQL uses passwords in two phases of client/server communication:
When a client attempts to connect to the server, there is an
initial authentication step in which the client must present
a password that has a hash value matching the hash value
stored in the user table for the account
the client wants to use.
After the client connects, it can (if it has sufficient
privileges) set or change the password hash for accounts
listed in the user table. The client can
do this by using the
PASSWORD() function to
generate a password hash, or by using a password-generating
statement (CREATE USER,
GRANT, or
SET PASSWORD).
In other words, the server checks hash
values during authentication when a client first attempts to
connect. The server generates hash values
if a connected client invokes the
PASSWORD() function or uses a
password-generating statement to set or change a password.
Password hashing methods in MySQL have the history described
following. These changes are illustrated by changes in the
result from the PASSWORD()
function that computes password hash values and in the structure
of the user table where passwords are stored.
The original hashing method produced a 16-byte string. Such hashes look like this:
mysql> SELECT PASSWORD('mypass');
+--------------------+
| PASSWORD('mypass') |
+--------------------+
| 6f8c114b58f2ce9e |
+--------------------+
To store account passwords, the Password
column of the user table was at this point 16
bytes long.
MySQL 4.1 introduced password hashing that provided better security and reduced the risk of passwords being intercepted. There were several aspects to this change:
Different format of password values produced by the
PASSWORD() function
Widening of the Password column
Control over the default hashing method
Control over the permitted hashing methods for clients attempting to connect to the server
The changes in MySQL 4.1 took place in two stages:
MySQL 4.1.0 used a preliminary version of the 4.1 hashing method. This method was short lived and the following discussion says nothing more about it.
In MySQL 4.1.1, the hashing method was modified to produce a longer 41-byte hash value:
mysql> SELECT PASSWORD('mypass');
+-------------------------------------------+
| PASSWORD('mypass') |
+-------------------------------------------+
| *6C8989366EAF75BB670AD8EA7A7FC1176A95CEF4 |
+-------------------------------------------+
The longer password hash format has better cryptographic properties, and client authentication based on long hashes is more secure than that based on the older short hashes.
To accommodate longer password hashes, the
Password column in the
user table was changed at this point to
be 41 bytes, its current length.
A widened Password column can store
password hashes in both the pre-4.1 and 4.1 formats. The
format of any given hash value can be determined two ways:
The length: 4.1 and pre-4.1 hashes are 41 and 16 bytes, respectively.
Password hashes in the 4.1 format always begin with a
“*” character, whereas
passwords in the pre-4.1 format never do.
To permit explicit generation of pre-4.1 password hashes, two additional changes were made:
The OLD_PASSWORD()
function was added, which returns hash values in the
16-byte format.
For compatibility purposes, the
old_passwords system
variable was added, to enable DBAs and applications
control over the hashing method. The default
old_passwords value of
0 causes hashing to use the 4.1 method (41-byte hash
values), but setting
old_passwords=1 causes
hashing to use the pre-4.1 method. In this case,
PASSWORD() produces
16-byte values and is equivalent to
OLD_PASSWORD()
To permit DBAs control over how clients are permitted to
connect, the secure_auth
system variable was added. Starting the server with this
variable disabled or enabled permits or prohibits clients to
connect using the older pre-4.1 password hashing method.
Before MySQL 5.6.5,
secure_auth is disabled by
default. As of 5.6.5,
secure_auth is enabled by
default to promote a more secure default configuration DBAs
can disable it at their discretion, but this is not
recommended, and pre-4.1 password hashes are deprecated and
should be avoided. (For account upgrade instructions, see
Section 6.1.3, “Migrating Away from Pre-4.1 Password Hashing and the mysql_old_password
Plugin”.)
In addition, the mysql client supports a
--secure-auth option that is
analogous to secure_auth,
but from the client side. It can be used to prevent
connections to less secure accounts that use pre-4.1
password hashing. This option is disabled by default before
MySQL 5.6.7, enabled thereafter.
The widening of the Password column in MySQL
4.1 from 16 bytes to 41 bytes affects installation or upgrade
operations as follows:
If you perform a new installation of MySQL, the
Password column is made 41 bytes long
automatically.
Upgrades from MySQL 4.1 or later to current versions of
MySQL should not give rise to any issues in regard to the
Password column because both versions use
the same column length and password hashing method.
For upgrades from a pre-4.1 release to 4.1 or later, you must upgrade the system tables after upgrading. (See mysql_upgrade — Check and Upgrade MySQL Tables.)
The 4.1 hashing method is understood only by MySQL 4.1 (and higher) servers and clients, which can result in some compatibility problems. A 4.1 or higher client can connect to a pre-4.1 server, because the client understands both the pre-4.1 and 4.1 password hashing methods. However, a pre-4.1 client that attempts to connect to a 4.1 or higher server may run into difficulties. For example, a 4.0 mysql client may fail with the following error message:
shell> mysql -h localhost -u root
Client does not support authentication protocol requested
by server; consider upgrading MySQL client
This phenomenon also occurs for attempts to use the older PHP
mysql extension after upgrading to MySQL 4.1
or higher. (See Common Problems with MySQL and PHP.)
The following discussion describes the differences between the pre-4.1 and 4.1 hashing methods, and what you should do if you upgrade your server but need to maintain backward compatibility with pre-4.1 clients. (However, permitting connections by old clients is not recommended and should be avoided if possible.) Additional information can be found in Client does not support authentication protocol. This information is of particular importance to PHP programmers migrating MySQL databases from versions older than 4.1 to 4.1 or higher.
The differences between short and long password hashes are relevant both for how the server uses passwords during authentication and for how it generates password hashes for connected clients that perform password-changing operations.
The way in which the server uses password hashes during
authentication is affected by the width of the
Password column:
If the column is short, only short-hash authentication is used.
If the column is long, it can hold either short or long hashes, and the server can use either format:
Pre-4.1 clients can connect, but because they know only about the pre-4.1 hashing method, they can authenticate only using accounts that have short hashes.
4.1 and later clients can authenticate using accounts that have short or long hashes.
Even for short-hash accounts, the authentication process is actually a bit more secure for 4.1 and later clients than for older clients. In terms of security, the gradient from least to most secure is:
Pre-4.1 client authenticating with short password hash
4.1 or later client authenticating with short password hash
4.1 or later client authenticating with long password hash
The way in which the server generates password hashes for
connected clients is affected by the width of the
Password column and by the
old_passwords system variable.
A 4.1 or later server generates long hashes only if certain
conditions are met: The Password column must
be wide enough to hold long values and
old_passwords must not be set
to 1.
Those conditions apply as follows:
The Password column must be wide enough
to hold long hashes (41 bytes). If the column has not been
updated and still has the pre-4.1 width of 16 bytes, the
server notices that long hashes cannot fit into it and
generates only short hashes when a client performs
password-changing operations using the
PASSWORD() function or a
password-generating statement. This is the behavior that
occurs if you have upgraded from a version of MySQL older
than 4.1 to 4.1 or later but have not yet run the
mysql_upgrade program to widen the
Password column.
If the Password column is wide, it can
store either short or long password hashes. In this case,
the PASSWORD() function and
password-generating statements generate long hashes unless
the server was started with the
old_passwords system
variable set to 1 to force the server to generate short
password hashes instead.
The purpose of the
old_passwords system variable
is to permit backward compatibility with pre-4.1 clients under
circumstances where the server would otherwise generate long
password hashes. The option does not affect authentication (4.1
and later clients can still use accounts that have long password
hashes), but it does prevent creation of a long password hash in
the user table as the result of a
password-changing operation. Were that permitted to occur, the
account could no longer be used by pre-4.1 clients. With
old_passwords disabled, the
following undesirable scenario is possible:
An old pre-4.1 client connects to an account that has a short password hash.
The client changes its own password. With
old_passwords disabled,
this results in the account having a long password hash.
The next time the old client attempts to connect to the account, it cannot, because the account has a long password hash that requires the 4.1 hashing method during authentication. (Once an account has a long password hash in the user table, only 4.1 and later clients can authenticate for it because pre-4.1 clients do not understand long hashes.)
This scenario illustrates that, if you must support older
pre-4.1 clients, it is problematic to run a 4.1 or higher server
without old_passwords set to 1.
By running the server with
old_passwords=1,
password-changing operations do not generate long password
hashes and thus do not cause accounts to become inaccessible to
older clients. (Those clients cannot inadvertently lock
themselves out by changing their password and ending up with a
long password hash.)
The downside of old_passwords=1
is that any passwords created or changed use short hashes, even
for 4.1 or later clients. Thus, you lose the additional security
provided by long password hashes. To create an account that has
a long hash (for example, for use by 4.1 clients) or to change
an existing account to use a long password hash, an
administrator can set the session value of
old_passwords set to 0 while
leaving the global value set to 1:
mysql>SET @@session.old_passwords = 0;Query OK, 0 rows affected (0.00 sec) mysql>SELECT @@session.old_passwords, @@global.old_passwords;+-------------------------+------------------------+ | @@session.old_passwords | @@global.old_passwords | +-------------------------+------------------------+ | 0 | 1 | +-------------------------+------------------------+ 1 row in set (0.00 sec) mysql>CREATE USER 'newuser'@'localhost' IDENTIFIED BY 'newpass';Query OK, 0 rows affected (0.03 sec) mysql>SET PASSWORD FOR 'existinguser'@'localhost' = PASSWORD('existingpass');Query OK, 0 rows affected (0.00 sec)
The following scenarios are possible in MySQL 4.1 or later. The
factors are whether the Password column is
short or long, and, if long, whether the server is started with
old_passwords enabled or
disabled.
Scenario 1: Short
Password column in user table:
Only short hashes can be stored in the
Password column.
The server uses only short hashes during client authentication.
For connected clients, password hash-generating operations
involving the PASSWORD()
function or password-generating statements use short hashes
exclusively. Any change to an account's password results in
that account having a short password hash.
The value of old_passwords
is irrelevant because with a short
Password column, the server generates
only short password hashes anyway.
This scenario occurs when a pre-4.1 MySQL installation has been
upgraded to 4.1 or later but mysql_upgrade
has not been run to upgrade the system tables in the
mysql database. (This is not a recommended
configuration because it does not permit use of more secure 4.1
password hashing.)
Scenario 2: Long
Password column; server started with
old_passwords=1:
Short or long hashes can be stored in the
Password column.
4.1 and later clients can authenticate for accounts that have short or long hashes.
Pre-4.1 clients can authenticate only for accounts that have short hashes.
For connected clients, password hash-generating operations
involving the PASSWORD()
function or password-generating statements use short hashes
exclusively. Any change to an account's password results in
that account having a short password hash.
In this scenario, newly created accounts have short password
hashes because old_passwords=1
prevents generation of long hashes. Also, if you create an
account with a long hash before setting
old_passwords to 1, changing
the account's password while
old_passwords=1 results in the
account being given a short password, causing it to lose the
security benefits of a longer hash.
To create a new account that has a long password hash, or to
change the password of any existing account to use a long hash,
first set the session value of
old_passwords set to 0 while
leaving the global value set to 1, as described previously.
In this scenario, the server has an up to date
Password column, but is running with the
default password hashing method set to generate pre-4.1 hash
values. This is not a recommended configuration but may be
useful during a transitional period in which pre-4.1 clients and
passwords are upgraded to 4.1 or later. When that has been done,
it is preferable to run the server with
old_passwords=0 and
secure_auth=1.
Scenario 3: Long
Password column; server started with
old_passwords=0:
Short or long hashes can be stored in the
Password column.
4.1 and later clients can authenticate using accounts that have short or long hashes.
Pre-4.1 clients can authenticate only using accounts that have short hashes.
For connected clients, password hash-generating operations
involving the PASSWORD()
function or password-generating statements use long hashes
exclusively. A change to an account's password results in
that account having a long password hash.
As indicated earlier, a danger in this scenario is that it is
possible for accounts that have a short password hash to become
inaccessible to pre-4.1 clients. A change to such an account's
password made using the
PASSWORD() function or a
password-generating statement results in the account being given
a long password hash. From that point on, no pre-4.1 client can
connect to the server using that account. The client must
upgrade to 4.1 or later.
If this is a problem, you can change a password in a special
way. For example, normally you use SET
PASSWORD as follows to change an account password:
SET PASSWORD FOR 'some_user'@'some_host' = PASSWORD('mypass');
To change the password but create a short hash, use the
OLD_PASSWORD() function instead:
SET PASSWORD FOR 'some_user'@'some_host' = OLD_PASSWORD('mypass');
OLD_PASSWORD() is useful for
situations in which you explicitly want to generate a short
hash.
The disadvantages for each of the preceding scenarios may be summarized as follows:
In scenario 1, you cannot take advantage of longer hashes that provide more secure authentication.
In scenario 2, old_passwords=1
prevents accounts with short hashes from becoming inaccessible,
but password-changing operations cause accounts with long hashes
to revert to short hashes unless you take care to change the
session value of old_passwords
to 0 first.
In scenario 3, accounts with short hashes become inaccessible to
pre-4.1 clients if you change their passwords without explicitly
using OLD_PASSWORD().
The best way to avoid compatibility problems related to short password hashes is to not use them:
Upgrade all client programs to MySQL 4.1 or later.
Run the server with
old_passwords=0.
Reset the password for any account with a short password hash to use a long password hash.
For additional security, run the server with
secure_auth=1.
When you connect to a MySQL server, you should use a password. The password is not transmitted in clear text over the connection. Password handling during the client connection sequence was upgraded in MySQL 4.1.1 to be very secure. If you are still using pre-4.1.1-style passwords, the encryption algorithm is not as strong as the newer algorithm. With some effort, a clever attacker who can sniff the traffic between the client and the server can crack the password. (See Section 2.2.4, “Password Hashing in MySQL”, for a discussion of the different password handling methods.)
All other information is transferred as text, and can be read by anyone who is able to watch the connection. If the connection between the client and the server goes through an untrusted network, and you are concerned about this, you can use the compressed protocol to make traffic much more difficult to decipher. You can also use MySQL's internal SSL support to make the connection even more secure. See Section 5.11, “Using Secure Connections”. Alternatively, use SSH to get an encrypted TCP/IP connection between a MySQL server and a MySQL client. You can find an Open Source SSH client at http://www.openssh.org/, and a comparison of both Open Source and Commercial SSH clients at http://en.wikipedia.org/wiki/Comparison_of_SSH_clients.
To make a MySQL system secure, you should strongly consider the following suggestions:
Require all MySQL accounts to have a password. A client
program does not necessarily know the identity of the person
running it. It is common for client/server applications that
the user can specify any user name to the client program. For
example, anyone can use the mysql program
to connect as any other person simply by invoking it as
mysql -u if
other_user
db_nameother_user has no password. If all
accounts have a password, connecting using another user's
account becomes much more difficult.
For a discussion of methods for setting passwords, see Section 5.5, “Assigning Account Passwords”.
Make sure that the only Unix user account with read or write privileges in the database directories is the account that is used for running mysqld.
Never run the MySQL server as the Unix root
user. This is extremely dangerous, because any user with the
FILE privilege is able to cause
the server to create files as root (for
example, ~root/.bashrc). To prevent this,
mysqld refuses to run as
root unless that is specified explicitly
using the --user=root option.
mysqld can (and should) be run as an
ordinary, unprivileged user instead. You can create a separate
Unix account named mysql to make everything
even more secure. Use this account only for administering
MySQL. To start mysqld as a different Unix
user, add a user option that specifies the
user name in the [mysqld] group of the
my.cnf option file where you specify
server options. For example:
[mysqld] user=mysql
This causes the server to start as the designated user whether you start it manually or by using mysqld_safe or mysql.server. For more details, see Section 2.5, “How to Run MySQL as a Normal User”.
Running mysqld as a Unix user other than
root does not mean that you need to change
the root user name in the
user table. User names for MySQL
accounts have nothing to do with user names for Unix
accounts.
Do not grant the FILE privilege
to nonadministrative users. Any user that has this privilege
can write a file anywhere in the file system with the
privileges of the mysqld daemon. This
includes the server's data directory containing the files that
implement the privilege tables. To make
FILE-privilege operations a bit
safer, files generated with
SELECT ... INTO
OUTFILE do not overwrite existing files and are
writable by everyone.
The FILE privilege may also be
used to read any file that is world-readable or accessible to
the Unix user that the server runs as. With this privilege,
you can read any file into a database table. This could be
abused, for example, by using LOAD
DATA to load /etc/passwd into a
table, which then can be displayed with
SELECT.
To limit the location in which files can be read and written,
set the secure_file_priv
system to a specific directory. See
Server System Variables.
Do not grant the PROCESS or
SUPER privilege to
nonadministrative users. The output of mysqladmin
processlist and SHOW
PROCESSLIST shows the text of any statements
currently being executed, so any user who is permitted to see
the server process list might be able to see statements issued
by other users such as UPDATE user SET
password=PASSWORD('not_secure').
mysqld reserves an extra connection for
users who have the SUPER
privilege, so that a MySQL root user can
log in and check server activity even if all normal
connections are in use.
The SUPER privilege can be used
to terminate client connections, change server operation by
changing the value of system variables, and control
replication servers.
Do not permit the use of symlinks to tables. (This capability
can be disabled with the
--skip-symbolic-links
option.) This is especially important if you run
mysqld as root, because
anyone that has write access to the server's data directory
then could delete any file in the system! See
Using Symbolic Links for MyISAM Tables on Unix.
Stored programs and views should be written using the security guidelines discussed in Access Control for Stored Programs and Views.
If you do not trust your DNS, you should use IP addresses rather than host names in the grant tables. In any case, you should be very careful about creating grant table entries using host name values that contain wildcards.
If you want to restrict the number of connections permitted to
a single account, you can do so by setting the
max_user_connections variable
in mysqld. The
GRANT statement also supports
resource control options for limiting the extent of server use
permitted to an account. See GRANT Syntax.
If the plugin directory is writable by the server, it may be
possible for a user to write executable code to a file in the
directory using SELECT
... INTO DUMPFILE. This can be prevented by making
plugin_dir read only to the
server or by setting
--secure-file-priv to a
directory where SELECT writes
can be made safely.
The following table shows mysqld options and system variables that affect security. For descriptions of each of these, see Server Command Options, and Server System Variables.
Table 2.1 Security Option/Variable Summary
| Name | Cmd-Line | Option File | System Var | Status Var | Var Scope | Dynamic |
|---|---|---|---|---|---|---|
| allow-suspicious-udfs | Yes | Yes | ||||
| automatic_sp_privileges | Yes | Global | Yes | |||
| chroot | Yes | Yes | ||||
| des-key-file | Yes | Yes | ||||
| local_infile | Yes | Global | Yes | |||
| old_passwords | Yes | Both | Yes | |||
| safe-user-create | Yes | Yes | ||||
| secure-auth | Yes | Yes | Global | Yes | ||
| - Variable: secure_auth | Yes | Global | Yes | |||
| secure-file-priv | Yes | Yes | Global | No | ||
| - Variable: secure_file_priv | Yes | Global | No | |||
| skip-grant-tables | Yes | Yes | ||||
| skip-name-resolve | Yes | Yes | Global | No | ||
| - Variable: skip_name_resolve | Yes | Global | No | |||
| skip-networking | Yes | Yes | Global | No | ||
| - Variable: skip_networking | Yes | Global | No | |||
| skip-show-database | Yes | Yes | Global | No | ||
| - Variable: skip_show_database | Yes | Global | No |
On Windows, you can run the server as a Windows service using a normal user account.
On Linux, for installations performed using a MySQL repository,
RPM packages, or Debian packages, the MySQL server
mysqld should be started by the local
mysql operating system user. Starting by
another operating system user is not supported by the init scripts
that are included as part of the installation.
On Unix (or Linux for installations performed using
tar or tar.gz packages)
, the MySQL server mysqld can be started and
run by any user. However, you should avoid running the server as
the Unix root user for security reasons. To
change mysqld to run as a normal unprivileged
Unix user user_name, you must do the
following:
Stop the server if it is running (use mysqladmin shutdown).
Change the database directories and files so that
user_name has privileges to read
and write files in them (you might need to do this as the Unix
root user):
shell> chown -R user_name /path/to/mysql/datadir
If you do not do this, the server will not be able to access
databases or tables when it runs as
user_name.
If directories or files within the MySQL data directory are
symbolic links, chown -R might not follow
symbolic links for you. If it does not, you will also need to
follow those links and change the directories and files they
point to.
Start the server as user user_name.
Another alternative is to start mysqld as
the Unix root user and use the
--user=
option. mysqld starts up, then switches to
run as the Unix user user_nameuser_name
before accepting any connections.
To start the server as the given user automatically at system
startup time, specify the user name by adding a
user option to the
[mysqld] group of the
/etc/my.cnf option file or the
my.cnf option file in the server's data
directory. For example:
[mysqld]
user=user_name
If your Unix machine itself is not secured, you should assign
passwords to the MySQL root account in the
grant tables. Otherwise, any user with a login account on that
machine can run the mysql client with a
--user=root option and perform any
operation. (It is a good idea to assign passwords to MySQL
accounts in any case, but especially so when other login accounts
exist on the server host.) See
Section 3.4, “Securing the Initial MySQL Accounts”.
The LOAD DATA statement can load a
file that is located on the server host, or it can load a file
that is located on the client host when the
LOCAL keyword is specified.
There are two potential security issues with supporting the
LOCAL version of LOAD
DATA statements:
The transfer of the file from the client host to the server
host is initiated by the MySQL server. In theory, a patched
server could be built that would tell the client program to
transfer a file of the server's choosing rather than the file
named by the client in the LOAD
DATA statement. Such a server could access any file
on the client host to which the client user has read access.
In a Web environment where the clients are connecting from a
Web server, a user could use
LOAD DATA
LOCAL to read any files that the Web server process
has read access to (assuming that a user could run any command
against the SQL server). In this environment, the client with
respect to the MySQL server actually is the Web server, not
the remote program being run by the user who connects to the
Web server.
To deal with these problems,
LOAD DATA
LOCAL works like this:
By default, all MySQL clients and libraries in binary
distributions are compiled with the
-DENABLED_LOCAL_INFILE=1 option.
If you build MySQL from source but do not invoke
CMake with the
-DENABLED_LOCAL_INFILE=1 option,
LOAD DATA
LOCAL cannot be used by any client unless it is
written explicitly to invoke
mysql_options(...
MYSQL_OPT_LOCAL_INFILE, 0). See
mysql_options().
You can disable all
LOAD DATA
LOCAL statements from the server side by starting
mysqld with the
--local-infile=0 option.
For the mysql command-line client, enable
LOAD DATA
LOCAL by specifying the
--local-infile[=1] option, or
disable it with the
--local-infile=0 option. For
mysqlimport, local data file loading is off
by default; enable it with the
--local or
-L option. In any case, successful use of a
local load operation requires that the server permits it.
If you use LOAD
DATA LOCAL in Perl scripts or other programs that
read the [client] group from option files,
you can add the local-infile=1 option to
that group. However, to keep this from causing problems for
programs that do not understand
local-infile, specify it using the
loose- prefix:
[client] loose-local-infile=1
If LOAD DATA
LOCAL is disabled, either in the server or the
client, a client that attempts to issue such a statement
receives the following error message:
ERROR 1148: The used command is not allowed with this MySQL version
Applications that access MySQL should not trust any data entered
by users, who can try to trick your code by entering special or
escaped character sequences in Web forms, URLs, or whatever
application you have built. Be sure that your application remains
secure if a user enters something like “; DROP
DATABASE mysql;”. This is an extreme example, but
large security leaks and data loss might occur as a result of
hackers using similar techniques, if you do not prepare for them.
A common mistake is to protect only string data values. Remember
to check numeric data as well. If an application generates a query
such as SELECT * FROM table WHERE ID=234 when a
user enters the value 234, the user can enter
the value 234 OR 1=1 to cause the application
to generate the query SELECT * FROM table WHERE ID=234 OR
1=1. As a result, the server retrieves every row in the
table. This exposes every row and causes excessive server load.
The simplest way to protect from this type of attack is to use
single quotation marks around the numeric constants:
SELECT * FROM table WHERE ID='234'. If the user
enters extra information, it all becomes part of the string. In a
numeric context, MySQL automatically converts this string to a
number and strips any trailing nonnumeric characters from it.
Sometimes people think that if a database contains only publicly available data, it need not be protected. This is incorrect. Even if it is permissible to display any row in the database, you should still protect against denial of service attacks (for example, those that are based on the technique in the preceding paragraph that causes the server to waste resources). Otherwise, your server becomes unresponsive to legitimate users.
Checklist:
Enable strict SQL mode to tell the server to be more restrictive of what data values it accepts. See Server SQL Modes.
Try to enter single and double quotation marks
(“'” and
“"”) in all of your Web forms.
If you get any kind of MySQL error, investigate the problem
right away.
Try to modify dynamic URLs by adding %22
(“"”), %23
(“#”), and
%27 (“'”)
to them.
Try to modify data types in dynamic URLs from numeric to character types using the characters shown in the previous examples. Your application should be safe against these and similar attacks.
Try to enter characters, spaces, and special symbols rather than numbers in numeric fields. Your application should remove them before passing them to MySQL or else generate an error. Passing unchecked values to MySQL is very dangerous!
Check the size of data before passing it to MySQL.
Have your application connect to the database using a user name different from the one you use for administrative purposes. Do not give your applications any access privileges they do not need.
Many application programming interfaces provide a means of escaping special characters in data values. Properly used, this prevents application users from entering values that cause the application to generate statements that have a different effect than you intend:
MySQL C API: Use the
mysql_real_escape_string() API
call.
MySQL++: Use the escape and
quote modifiers for query streams.
PHP: Use either the mysqli or
pdo_mysql extensions, and not the older
ext/mysql extension. The preferred API's
support the improved MySQL authentication protocol and
passwords, as well as prepared statements with placeholders.
See also Choosing an API.
If the older ext/mysql extension must be
used, then for escaping use the
mysql_real_escape_string()
function and not
mysql_escape_string() or
addslashes() because only
mysql_real_escape_string() is
character set-aware; the other functions can be
“bypassed” when using (invalid) multibyte
character sets.
Perl DBI: Use placeholders or the quote()
method.
Ruby DBI: Use placeholders or the quote()
method.
Java JDBC: Use a PreparedStatement object
and placeholders.
Other programming interfaces might have similar capabilities.