6.1 General Security Issues

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 *note postinstallation::.

For answers to some questions that are often asked about MySQL Server security issues, see *note faqs-security::.

 File: manual.info.tmp, Node: security-guidelines, Next: password-security, Prev: general-security-issues, Up: general-security-issues

6.1.1 Security Guidelines

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:

 File: manual.info.tmp, Node: password-security, Next: security-against-attack, Prev: security-guidelines, Up: general-security-issues

6.1.2 Keeping Passwords Secure

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.

 File: manual.info.tmp, Node: password-security-user, Next: password-security-admin, Prev: password-security, Up: password-security

6.1.2.1 End-User Guidelines for Password Security .................................................

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.

On Unix, the note 'mysql': mysql. client writes a record of executed statements to a history file (see note 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 note 'CREATE USER': create-user. and note 'ALTER USER': 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 contains MySQL passwords entered on the command line. For example, 'bash' uses '~/.bash_history'. Any such file should have a restrictive access mode.

 File: manual.info.tmp, Node: password-security-admin, Next: password-logging, Prev: password-security-user, Up: password-security

6.1.2.2 Administrator Guidelines for Password Security ......................................................

Database administrators should use the following guidelines to keep passwords secure.

MySQL stores passwords for user accounts in the 'mysql.user' system 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 note password-management::, and note expired-password-handling::.

The 'validate_password' plugin can be used to enforce a policy on acceptable password. See *note validate-password::.

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 plugin directory location 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 *note password-logging::.

 File: manual.info.tmp, Node: password-logging, Next: password-hashing, Prev: password-security-admin, Up: password-security

6.1.2.3 Passwords and Logging .............................

Passwords can be written as plain text in SQL statements such as note 'CREATE USER': create-user, note 'GRANT': grant, *note 'SET PASSWORD': 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.

Statement logging avoids writing passwords as 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, note 'INSERT': insert. or note 'UPDATE': update. statements for the 'mysql.user' system table that refer to literal passwords are logged as is, so you should avoid such statements. (Direct modification 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 *note audit-log-security::.

Statements received by the server may be rewritten if a query rewrite plugin is installed (see Query Rewrite Plugins (https://dev.mysql.com/doc/extending-mysql/5.7/en/plugin-types.html#query-rewrite-plugin-type)). 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.

Replicas store the password for the replication source in the source info repository, which can be either a file or a table (see note replica-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 note 'START SLAVE': start-slave. statement to specify credentials for connecting to the source.

Use a restricted access mode to protect database backups that include log tables or log files containing passwords.

 File: manual.info.tmp, Node: password-hashing, Prev: password-logging, Up: password-security

6.1.2.4 Password Hashing in MySQL .................................

Note:

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 was 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:

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 (Pre-4.1) Hashing Method

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.

The 4.1 Hashing Method

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:

The changes in MySQL 4.1 took place in two stages:

Compatibility Issues Related to Hashing Methods

The widening of the 'Password' column in MySQL 4.1 from 16 bytes to 41 bytes affects installation or upgrade operations as follows:

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 *note 'mysql': mysql. client may fail with the following error message:

 $> mysql -h localhost -u root
 Client does not support authentication protocol requested
 by server; consider upgrading MySQL client

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.) 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:

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:

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 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:

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:

This scenario occurs when a pre-4.1 MySQL installation has been upgraded to 4.1 or later but *note 'mysql_upgrade': 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':

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':

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 *note 'SET PASSWORD': set-password. as follows to change an account password:

 SET PASSWORD FOR 'SOME_USER'@'SOME_HOST' = PASSWORD('PASSWORD');

To change the password but create a short hash, use the 'OLD_PASSWORD()' function instead:

 SET PASSWORD FOR 'SOME_USER'@'SOME_HOST' = OLD_PASSWORD('PASSWORD');

'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:

 File: manual.info.tmp, Node: security-against-attack, Next: security-options, Prev: password-security, Up: general-security-issues

6.1.3 Making MySQL Secure Against Attackers

When you connect to a MySQL server, you should use a password. The password is not transmitted as cleartext 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 *note password-hashing::, 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 *note encrypted-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:

 File: manual.info.tmp, Node: security-options, Next: changing-mysql-user, Prev: security-against-attack, Up: general-security-issues

6.1.5 How to Run MySQL as a Normal User

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 *note 'mysqld': 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 note 'mysqld': 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 note 'mysqld': mysqld. to run as a normal unprivileged Unix user USER_NAME, you must do the following:

  1. Stop the server if it is running (use *note 'mysqladmin shutdown': mysqladmin.).

  2. 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):

      $> chown -R USER_NAME /PATH/TO/MYSQL/DATADIR

    If you do not do this, the server is unable 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 must also follow those links and change the directories and files they point to.

  3. Start the server as user USER_NAME. Another alternative is to start note 'mysqld': mysqld. as the Unix 'root' user and use the '--user=USER_NAME' option. note 'mysqld': mysqld. starts, then switches to run as the Unix user USER_NAME before accepting any connections.

  4. 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 note 'mysql': 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 note default-privileges::.

 File: manual.info.tmp, Node: load-data-local-security, Next: secure-client-programming, Prev: changing-mysql-user, Up: general-security-issues

6.1.6 Security Considerations for LOAD DATA LOCAL

The *note 'LOAD DATA': load-data. statement loads a data file into a table. The statement can load a file located on the server host, or, if the 'LOCAL' keyword is specified, on the client host.

The 'LOCAL' version of *note 'LOAD DATA': load-data. has two potential security issues:

To avoid connecting to untrusted servers, clients can establish a secure connection and verify the server identity by connecting using the '--ssl-mode=VERIFY_IDENTITY' option and the appropriate CA certificate. To implement this level of verification, you must first ensure that the CA certificate for the server is reliably available to the replica, otherwise availability issues will result. For more information, see *note encrypted-connection-options::.

To avoid *note 'LOAD DATA': load-data. issues, clients should avoid using 'LOCAL'.

Adminstrators and applications can configure whether to permit local data loading as follows:

If 'LOCAL' capability is disabled, on either the server or client side, a client that attempts to issue a *note 'LOAD DATA LOCAL': load-data. statement receives the following error message:

 ERROR 1148: The used command is not allowed with this MySQL version

MySQL Shell and Local Data Loading

MySQL Shell provides a number of utilities to dump tables, schemas, or server instances and load them into other instances. When you use these utilities to handle the data, MySQL Shell provides additional functions such as input preprocessing, multithreaded parallel loading, file compression and decompression, and handling access to Oracle Cloud Infrastructure Object Storage buckets. To get the best functionality, always use the most recent version available of MySQL Shell's dump and dump loading utilities.

MySQL Shell's data upload utilities use *note 'LOAD DATA LOCAL INFILE': load-data. statements to upload data, so the 'local_infile' system variable must be set to 'ON' on the target server instance. You can do this before uploading the data, and remove it again afterwards. The utilities handle the file transfer requests safely to deal with the security considerations discussed in this topic.

MySQL Shell includes these dump and dump loading utilities:

  1. Table export utility 'util.exportTable()'

    Exports a MySQL relational table into a data file, which can be uploaded to a MySQL server instance using MySQL Shell's parallel table import utility, imported to a different application, or used as a logical backup. The utility has preset options and customization options to produce different output formats.

  2. Parallel table import utility 'util.importTable()'

    Inports a data file to a MySQL relational table. The data file can be the output from MySQL Shell's table export utility or another format supported by the utility's preset and customization options. The utility can carry out input preprocessing before adding the data to the table. It can accept multiple data files to merge into a single relational table, and automatically decompresses compressed files.

  3. Instance dump utility 'util.dumpInstance()', schema dump utility 'util.dumpSchemas()', and table dump utility 'util.dumpTables()'

    Export an instance, schema, or table to a set of dump files, which can then be uploaded to a MySQL instance using MySQL Shell's dump loading utility. The utilities provide Oracle Cloud Infrastructure Object Storage streaming, MySQL HeatWave Service compatibility checks and modifications, and the ability to carry out a dry run to identify issues before proceeding with the dump.

  4. Dump loading utility 'util.loadDump()'

    Import dump files created using MySQL Shell's instance, schema, or table dump utility into a MySQL HeatWave Service DB System or a MySQL Server instance. The utility manages the upload process and provides data streaming from remote storage, parallel loading of tables or table chunks, progress state tracking, resume and reset capability, and the option of concurrent loading while the dump is still taking place. MySQL Shell's parallel table import utility can be used in combination with the dump loading utility to modify data before uploading it to the target MySQL instance.

For details of the utilities, see MySQL Shell Utilities (https://dev.mysql.com/doc/mysql-shell/8.0/en/mysql-shell-utilities.html).

 File: manual.info.tmp, Node: secure-client-programming, Prev: load-data-local-security, Up: general-security-issues

6.1.7 Client Programming Security Guidelines

Client applications that access MySQL should use the following guidelines to avoid interpreting external data incorrectly or exposing sensitive information.

Handle External Data Properly

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 tries to perform SQL injection by entering something like '; DROP DATABASE mysql;' into a form. 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:

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:

Other programming interfaces might have similar capabilities.

Handle MySQL Error Messages Properly

It is the application's responsibility to intercept errors that occur as a result of executing SQL statements with the MySQL database server and handle them appropriately.

The information returned in a MySQL error is not gratuitous because that information is key in debugging MySQL using applications. It would be nearly impossible, for example, to debug a common 10-way join *note 'SELECT': select. statement without providing information regarding which databases, tables, and other objects are involved with problems. Thus, MySQL errors must sometimes necessarily contain references to the names of those objects.

A simple but insecure approach for an application when it receives such an error from MySQL is to intercept it and display it verbatim to the client. However, revealing error information is a known application vulnerability type (CWE-209 (http://cwe.mitre.org/data/definitions/209.html)) and the application developer must ensure the application does not have this vulnerability.

For example, an application that displays a message such as this exposes both a database name and a table name to clients, which is information a client might attempt to exploit:

 ERROR 1146 (42S02): Table 'mydb.mytable' does not exist

Instead, the proper behavior for an application when it receives such an error from MySQL is to log appropriate information, including the error information, to a secure audit location only accessible to trusted personnel. The application can return something more generic such as 'Internal Error' to the user.

 File: manual.info.tmp, Node: access-control, Next: encrypted-connections, Prev: general-security-issues, Up: security