Chapter 7 Security Plugins

Table of Contents

7.1 Authentication Plugins
7.1.1 The Native Authentication Plugin
7.1.2 The Old Native Authentication Plugin
7.1.3 Migrating Away from Pre-4.1 Password Hashing and the mysql_old_password Plugin
7.1.4 The SHA-256 Authentication Plugin
7.1.5 The PAM Authentication Plugin
7.1.6 The Windows Native Authentication Plugin
7.1.7 The Cleartext Client-Side Authentication Plugin
7.1.8 The Socket Peer-Credential Authentication Plugin
7.1.9 The Test Authentication Plugin
7.2 The Password Validation Plugin
7.2.1 Password Validation Plugin Installation
7.2.2 Password Validation Plugin Options and Variables
7.3 MySQL Enterprise Audit
7.3.1 Installing MySQL Enterprise Audit
7.3.2 MySQL Enterprise Audit Security Considerations
7.3.3 The Audit Log File
7.3.4 Audit Log Logging Control
7.3.5 Audit Log Filtering
7.3.6 Audit Log Reference
7.3.7 Audit Log Restrictions
7.4 MySQL Enterprise Firewall
7.4.1 MySQL Enterprise Firewall Components
7.4.2 Installing or Uninstalling MySQL Enterprise Firewall
7.4.3 Using MySQL Enterprise Firewall
7.4.4 MySQL Enterprise Firewall Reference

MySQL includes several plugins that implement security features:

7.1 Authentication Plugins

The following sections describe the authentication plugins available in MySQL.

The default plugin is mysql_native_password unless the --default-authentication-plugin option is set otherwise at server startup.

7.1.1 The Native Authentication Plugin

MySQL includes two plugins that implement native authentication; that is, authentication against passwords stored in the Password column of the mysql.user table. This section describes mysql_native_password, which implements authentication against the mysql.user table using the native password hashing method. For information about mysql_old_password, which implements authentication using the older (pre-4.1) password hashing method, see Section 7.1.2, “The Old Native Authentication Plugin”. For information about these password hashing methods, see Section 2.2.4, “Password Hashing in MySQL”.

The mysql_native_password native authentication plugin is backward compatible. Clients older than MySQL 5.5.7 do not support authentication plugins but do use the native authentication protocol, so they can connect to servers from MySQL 5.5.7 and up.

The following table shows the plugin names on the server and client sides.

Table 7.1 MySQL Native Password Authentication Plugin

Server-side plugin namemysql_native_password
Client-side plugin namemysql_native_password
Library file nameNone (plugins are built in)

The plugin exists in both client and server form:

  • The server-side plugin is built into the server, need not be loaded explicitly, and cannot be disabled by unloading it.

  • The client-side plugin is built into the libmysqlclient client library as of MySQL 5.5.7 and available to any program linked against libmysqlclient from that version or higher.

  • MySQL client programs use mysql_native_password by default. The --default-auth option can be used as a hint about which client-side plugin the program can expect to use:

    shell> mysql --default-auth=mysql_native_password ...
    

If an account row specifies no plugin name, the server authenticates the account using either the mysql_native_password or mysql_old_password plugin, depending on whether the password hash value in the Password column used native hashing or the older pre-4.1 hashing method. Clients must match the password in the Password column of the account row.

For general information about pluggable authentication in MySQL, see Section 5.7, “Pluggable Authentication”.

7.1.2 The Old Native Authentication Plugin

MySQL includes two plugins that implement native authentication; that is, authentication against passwords stored in the Password column of the mysql.user table. This section describes mysql_old_password, which implements authentication against the mysql.user table using the older (pre-4.1) password hashing method. For information about mysql_native_password, which implements authentication using the native password hashing method, see Section 7.1.1, “The Native Authentication Plugin”. For information about these password hashing methods, see Section 2.2.4, “Password Hashing in MySQL”.

Note

Passwords that use the pre-4.1 hashing method are less secure than passwords that use the native password hashing method and should be avoided. Pre-4.1 passwords are deprecated and support for them will be removed in a future MySQL release. For account upgrade instructions, see Section 7.1.3, “Migrating Away from Pre-4.1 Password Hashing and the mysql_old_password Plugin”.

The mysql_old_password native authentication plugin is backward compatible. Clients older than MySQL 5.5.7 do not support authentication plugins but do use the native authentication protocol, so they can connect to servers from MySQL 5.5.7 and up.

The following table shows the plugin names on the server and client sides.

Table 7.2 MySQL Old Native Authentication Plugin

Server-side plugin namemysql_old_password
Client-side plugin namemysql_old_password
Library file nameNone (plugins are built in)

The plugin exists in both client and server form:

  • The server-side plugin is built into the server, need not be loaded explicitly, and cannot be disabled by unloading it.

  • The client-side plugin is built into the libmysqlclient client library as of MySQL 5.5.7 and available to any program linked against libmysqlclient from that version or higher.

  • MySQL client programs can use the --default-auth option to specify the mysql_old_password plugin as a hint about which client-side plugin the program can expect to use:

    shell> mysql --default-auth=mysql_old_password ...
    

If an account row specifies no plugin name, the server authenticates the account using either the mysql_native_password or mysql_old_password plugin, depending on whether the password hash value in the Password column used native hashing or the older pre-4.1 hashing method. Clients must match the password in the Password column of the account row.

For general information about pluggable authentication in MySQL, see Section 5.7, “Pluggable Authentication”.

7.1.3 Migrating Away from Pre-4.1 Password Hashing and the mysql_old_password Plugin

The MySQL server authenticates connection attempts for each account listed in the mysql.user table using the authentication plugin named in the plugin column. If the plugin column is empty, the server authenticates the account as follows:

  • Before MySQL 5.7.2, the server uses the mysql_native_password or mysql_old_password plugin implicitly, depending on the format of the password hash in the Password column. If the Password value is empty or a 4.1 password hash (41 characters), the server uses mysql_native_password. If the password value is a pre-4.1 password hash (16 characters), the server uses mysql_old_password. (For additional information about these hash formats, see Section 2.2.4, “Password Hashing in MySQL”.)

  • As of MySQL 5.7.2, the server requires the plugin column to be nonempty and disables accounts that have an empty plugin value.

Pre-4.1 password hashes and the mysql_old_password plugin are deprecated as of MySQL 5.6.5 and support for them is removed in MySQL 5.7.5. They provide a level of security inferior to that offered by 4.1 password hashing and the mysql_native_password plugin.

Given the requirement in MySQL 5.7.2 that the plugin column must be nonempty, coupled with removal of mysql_old_password support in 5.7.5, DBAs are advised to upgrade accounts as follows:

  • Upgrade accounts that use mysql_native_password implicitly to use it explicitly

  • Upgrade accounts that use mysql_old_password (either implicitly or explicitly) to use mysql_native_password explicitly

The instructions in this section describe how to perform those upgrades. The result is that no account has an empty plugin value and no account uses pre-4.1 password hashing or the mysql_old_password plugin.

As a variant on these instructions, DBAs might offer users the choice to upgrade to the sha256_password plugin, which authenticates using SHA-256 password hashes. For information about this plugin, see Section 7.1.4, “The SHA-256 Authentication Plugin”.

The following table lists the types of mysql.user accounts considered in this discussion.

plugin ColumnPassword ColumnAuthentication ResultUpgrade Action
EmptyEmptyImplicitly uses mysql_native_passwordAssign plugin
Empty4.1 hashImplicitly uses mysql_native_passwordAssign plugin
EmptyPre-4.1 hashImplicitly uses mysql_old_passwordAssign plugin, rehash password
mysql_native_passwordEmptyExplicitly uses mysql_native_passwordNone
mysql_native_password4.1 hashExplicitly uses mysql_native_passwordNone
mysql_old_passwordEmptyExplicitly uses mysql_old_passwordUpgrade plugin
mysql_old_passwordPre-4.1 hashExplicitly uses mysql_old_passwordUpgrade plugin, rehash password

Accounts corresponding to lines for the mysql_native_password plugin require no upgrade action (because no change of plugin or hash format is required). For accounts corresponding to lines for which the password is empty, consider asking the account owners to choose a password (or require it by using ALTER USER to expire empty account passwords).

Upgrading Accounts from Implicit to Explicit mysql_native_password Use

Accounts that have an empty plugin and a 4.1 password hash use mysql_native_password implicitly. To upgrade these accounts to use mysql_native_password explicitly, execute these statements:

UPDATE mysql.user SET plugin = 'mysql_native_password'
WHERE plugin = '' AND (Password = '' OR LENGTH(Password) = 41);
FLUSH PRIVILEGES;

Before MySQL 5.7.2, you can execute those statements to uprade accounts proactively. As of MySQL 5.7.2, you can run mysql_upgrade, which performs the same operation among its upgrade actions.

Notes:

  • The upgrade operation just described is safe to execute at any time because it makes the mysql_native_password plugin explicit only for accounts that already use it implicitly.

  • This operation requires no password changes, so it can be performed without affecting users or requiring their involvement in the upgrade process.

Upgrading Accounts from mysql_old_password to mysql_native_password

Accounts that use mysql_old_password (either implicitly or explicitly) should be upgraded to use mysql_native_password explicitly. This requires changing the plugin and changing the password from pre-4.1 to 4.1 hash format.

For the accounts covered in this step that must be upgraded, one of these conditions is true:

  • The account uses mysql_old_password implicitly because the plugin column is empty and the password has the pre-4.1 hash format (16 characters).

  • The account uses mysql_old_password explicitly.

To identify such accounts, use this query:

SELECT User, Host, Password FROM mysql.user
WHERE (plugin = '' AND LENGTH(Password) = 16)
OR plugin = 'mysql_old_password';

The following discussion provides two methods for updating that set of accounts. They have differing characteristics, so read both and decide which is most suitable for a given MySQL installation.

Method 1.

Characteristics of this method:

  • It requires that server and clients be run with secure_auth=0 until all users have been upgraded to mysql_native_password. (Otherwise, users cannot connect to the server using their old-format password hashes for the purpose of upgrading to a new-format hash.)

  • It works for MySQL 5.5 through 5.7.1. As of 5.7.2, it does not work because the server requires accounts to have a nonempty plugin and disables them otherwise. Therefore, if you have already upgraded to 5.7.2 or later, choose Method 2, described later.

You should ensure that the server is running with secure_auth=0.

For all accounts that use mysql_old_password explicitly, set them to the empty plugin:

UPDATE mysql.user SET plugin = ''
WHERE plugin = 'mysql_old_password';
FLUSH PRIVILEGES;

To also expire the password for affected accounts, use these statements instead:

UPDATE mysql.user SET plugin = '', password_expired = 'Y'
WHERE plugin = 'mysql_old_password';
FLUSH PRIVILEGES;

Now affected users can reset their password to use 4.1 hashing. Ask each user who now has an empty plugin to connect to the server and execute these statements:

SET old_passwords = 0;
SET PASSWORD = PASSWORD('user-chosen-password');
Note

In MySQL 5.6.5 or later, the client-side --secure-auth option is enabled by default, so remind users to disable it or they will be unable to connect:

shell> mysql -u user_name -p --secure-auth=0

After an affected user has executed those statements, you can set the corresponding account plugin to mysql_native_password to make the plugin explicit. Or you can periodically run these statements to find and fix any accounts for which affected users have reset their password:

UPDATE mysql.user SET plugin = 'mysql_native_password'
WHERE plugin = '' AND (Password = '' OR LENGTH(Password) = 41);
FLUSH PRIVILEGES;

When there are no more accounts with an empty plugin, this query returns an empty result:

SELECT User, Host, Password FROM mysql.user
WHERE (plugin = '' AND LENGTH(Password) = 16);

At that point, all accounts have been migrated away from pre-4.1 password hashing and the server no longer need be run with secure_auth=0.

Method 2.

Characteristics of this method:

  • It assigns each affected account a new password, so you must tell each such user the new password and ask the user to choose a new one. Communication of passwords to users is outside the scope of MySQL, but should be done carefully.

  • It does not require server or clients to be run with secure_auth=0.

  • It works for any version of MySQL 5.5 or later (and for 5.7.6 or later has an easier variant).

With this method, you update each account separately due to the need to set passwords individually. Choose a different password for each account.

Suppose that 'user1'@'localhost' is one of the accounts to be upgraded. Modify it as follows:

  • In MySQL 5.7.6 and higher, ALTER USER provides the capability of modifying both the account password and its authentication plugin, so you need not modify the mysql.user table directly:

    ALTER USER 'user1'@'localhost'
    IDENTIFIED WITH mysql_native_password BY 'DBA-chosen-password';
    

    To also expire the account password, use this statement instead:

    ALTER USER 'user1'@'localhost'
    IDENTIFIED WITH mysql_native_password BY 'DBA-chosen-password'
    PASSWORD EXPIRE;
    

    Then tell the user the new password and ask the user to connect to the server with that password and execute this statement to choose a new password:

    ALTER USER USER() IDENTIFIED BY 'user-chosen-password';
    
  • Before MySQL 5.7.6, you must modify the mysql.user table directly using these statements:

    SET old_passwords = 0;
    UPDATE mysql.user SET plugin = 'mysql_native_password',
    Password = PASSWORD('DBA-chosen-password')
    WHERE (User, Host) = ('user1', 'localhost');
    FLUSH PRIVILEGES;
    

    To also expire the account password, use these statements instead:

    SET old_passwords = 0;
    UPDATE mysql.user SET plugin = 'mysql_native_password',
    Password = PASSWORD('DBA-chosen-password'), password_expired = 'Y'
    WHERE (User, Host) = ('user1', 'localhost');
    FLUSH PRIVILEGES;
    

    Then tell the user the new password and ask the user to connect to the server with that password and execute these statements to choose a new password:

    SET old_passwords = 0;
    SET PASSWORD = PASSWORD('user-chosen-password');
    

Repeat for each account to be upgraded.

7.1.4 The SHA-256 Authentication Plugin

As of MySQL 5.6.6, MySQL provides an authentication plugin that implements SHA-256 hashing for user account passwords.

Important

To connect to the server using an account that authenticates with the sha256_password plugin, you must use either an SSL connection or an unencrypted connection that encrypts the password using RSA, as described later in this section. Either way, use of the sha256_password plugin requires that MySQL be built with SSL capabilities. See Chapter 6, Using Secure Connections.

The following table shows the plugin names on the server and client sides.

Table 7.3 MySQL SHA-256 Authentication Plugin

Server-side plugin namesha256_password
Client-side plugin namesha256_password
Library file nameNone (plugins are built in)

The server-side sha256_password plugin is built into the server, need not be loaded explicitly, and cannot be disabled by unloading it. Similarly, clients need not specify the location of the client-side plugin.

To set up an account that uses SHA-256 password hashing, use the following procedure.

  1. Create the account and specify that it authenticates using the sha256_password plugin:

    CREATE USER 'sha256user'@'localhost' IDENTIFIED WITH sha256_password;
    
  2. Set the old_passwords system variable to 2 to cause the PASSWORD() function to use SHA-256 hashing of password strings, then set the account password:

    SET old_passwords = 2;
    SET PASSWORD FOR 'sha256user'@'localhost' = PASSWORD('Sh@256Pa33');
    

Alternatively, start the server with the default authentication plugin set to sha256_password. For example, put these lines in the server option file:

[mysqld]
default-authentication-plugin=sha256_password

That causes the sha256_password plugin to be used by default for new accounts. As a result, it is possible to create the account and set its password without naming the plugin explicitly using this CREATE USER syntax:

CREATE USER 'sha256user'@'localhost' IDENTIFIED BY 'Sh@256Pa33';

In this case, the server assigns the sha256_password plugin to the account and encrypts the password using SHA-256.

Accounts in the mysql.user table that use SHA-256 passwords can be identified as rows with 'sha256_password' in the plugin column and a SHA-256 password hash in the authentication_string column.

Another consequence of using sha256_password as the default authentication plugin is that to create an account that uses a different plugin, you must specify that plugin using an IDENTIFIED WITH clause in the CREATE USER statement, then set old_passwords appropriately for the plugin before using SET PASSWORD to set the account password. For example, to use the mysql_native_password plugin, do this:

CREATE USER 'nativeuser'@'localhost' IDENTIFIED WITH mysql_native_password;
SET old_passwords = 0;
SET PASSWORD FOR 'nativeuser'@'localhost' = PASSWORD('N@tivePa33');

To set or change the password for any account that authenticates using the sha256_password plugin, be sure that the value of old_passwords is 2 before using SET PASSWORD. If old_passwords has a value other than 2, an error occurs for attempts to set the password:

mysql> SET old_passwords = 0;
mysql> SET PASSWORD FOR 'sha256user'@'localhost' = PASSWORD('NewSh@256Pa33');
ERROR 1827 (HY000): The password hash doesn't have the expected format.
Check if the correct password algorithm is being used with the
PASSWORD() function.

For more information about old_passwords and PASSWORD(), see Server System Variables, and Encryption and Compression Functions.

MySQL can be compiled using either OpenSSL or yaSSL (see Section 6.1, “OpenSSL Versus yaSSL”). The sha256_password plugin works with distributions compiled using either package, but if MySQL is compiled using OpenSSL, RSA encryption is available and sha256_password implements the following additional capabilities. (To enable these capabilities, you must also follow the RSA configuration procedure given later in this section.)

  • It is possible for the client to transmit passwords to the server using RSA encryption during the client connection process, as described later.

  • The server exposes two additional system variables, sha256_password_private_key_path and sha256_password_public_key_path. It is intended that the database administrator will set these to the names of the RSA private and public key-pair files at server startup if the key files have names that differ from the system variable default values.

  • The server exposes a status variable, Rsa_public_key, that displays the RSA public key value.

  • The mysql and mysqltest client programs support a --server-public-key-path option for specifying an RSA public key file explicitly. (This option was added in MySQL 5.6.6 under the name --server-public-key and renamed in 5.6.7 to --server-public-key-path.)

For clients that use the sha256_password plugin, passwords are never exposed as cleartext when connecting to the server. How password transmission occurs depends on whether an SSL connection is used and whether RSA encryption is available:

  • If an SSL connection is used, the password is sent as cleartext but cannot be snooped because the connection is encrypted using SSL.

  • If an SSL connection is not used but RSA encryption is available, the password is sent within an unencrypted connection, but the password is RSA-encrypted to prevent snooping. When the server receives the password, it decrypts it. A scramble is used in the encryption to prevent repeat attacks.

  • If an SSL connection is not used and RSA encryption is not available, the sha256_password plugin causes the connection attempt to fail because the password cannot be sent without being exposed as cleartext.

As mentioned previously, RSA password encryption is available only if MySQL was compiled using OpenSSL. The implication for MySQL distributions compiled using yaSSL is that SHA-256 passwords can be used only when clients use SSL to access the server. See Section 6.4, “Configuring MySQL to Use Secure Connections”.

Assuming that MySQL has been compiled using OpenSSL, the following procedure describes how to enable RSA encryption of passwords during the client connection process:

  1. Create the RSA private and public key-pair files using the instructions in Section 6.6, “Creating SSL and RSA Certificates and Keys”.

  2. If the private and public key files are located in the data directory and are named private_key.pem and public_key.pem (the default values of the sha256_password_private_key_path and sha256_password_public_key_path system variables), the server will use them automatically at startup.

    Otherwise, in the server option file, set the system variables to the key file names. If the files are located in the server data directory, you need not specify their full path names:

    [mysqld]
    sha256_password_private_key_path=myprivkey.pem
    sha256_password_public_key_path=mypubkey.pem
    

    If the key files are not located in the data directory, or to make their locations explicit in the system variable values, use full path names:

    [mysqld]
    sha256_password_private_key_path=/usr/local/mysql/myprivkey.pem
    sha256_password_public_key_path=/usr/local/mysql/mypubkey.pem
    
  3. Restart the server, then connect to it and check the Rsa_public_key status variable value. The value will differ from that shown here, but should be nonempty:

    mysql> SHOW STATUS LIKE 'Rsa_public_key'\G
    *************************** 1. row ***************************
    Variable_name: Rsa_public_key
            Value: -----BEGIN PUBLIC KEY-----
    MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDO9nRUDd+KvSZgY7cNBZMNpwX6
    MvE1PbJFXO7u18nJ9lwc99Du/E7lw6CVXw7VKrXPeHbVQUzGyUNkf45Nz/ckaaJa
    aLgJOBCIDmNVnyU54OT/1lcs2xiyfaDMe8fCJ64ZwTnKbY2gkt1IMjUAB5Ogd5kJ
    g8aV7EtKwyhHb0c30QIDAQAB
    -----END PUBLIC KEY-----
    

    If the value is empty, the server found some problem with the key files. Check the error log for diagnostic information.

After the server has been configured with the RSA key files, clients have the option of using them to connect to the server using accounts that authenticate with the sha256_password plugin. As mentioned previously, such accounts can use either an SSL connection (in which case RSA is not used) or an unencrypted connection that encrypts the password using RSA. Assume for the following discussion that SSL is not used. Connecting to the server involves no special preparation on the client side. For example:

shell> mysql --ssl-mode=DISABLED -u sha256user -p
Enter password: Sh@256Pa33

For connection attempts by sha256user, the server determines that sha256_password is the appropriate authentication plugin and invokes it. The plugin finds that the connection does not use SSL and thus requires the password to be transmitted using RSA encryption. In this case, the plugin sends the RSA public key to the client, which uses it to encrypt the password and returns the result to the server. The plugin uses the RSA key on the server side to decrypt the password and accepts or rejects the connection based on whether the password is correct.

The server sends the public key to the client as needed, but if a copy of the RSA public key is available on the client host, the client can use it to save a round trip in the client/server protocol:

shell> mysql --ssl-mode=DISABLED -u sha256user -p --server-public-key-path=file_name

The public key value in the file named by the --server-public-key-path option should be the same as the key value in the server-side file named by the sha256_password_public_key_path system variable. If the key file contains a valid public key value but the value is incorrect, an access-denied error occurs. If the key file does not contain a valid public key, the client program cannot use it. In this case, the sha256_password plugin sends the public key to the client as if no --server-public-key-path option had been specified.

Client users can get the RSA public key two ways:

  • The database administrator can provide a copy of the public key file.

  • A client user who can connect to the server some other way can use a SHOW STATUS LIKE 'Rsa_public_key' statement and save the returned key value in a file.

7.1.5 The PAM Authentication Plugin

Note

The PAM authentication plugin is an extension included in MySQL Enterprise Edition, a commercial product. To learn more about commercial products, see http://www.mysql.com/products/.

As of MySQL 5.6.10, MySQL Enterprise Edition includes an authentication plugin that enables MySQL Server to use PAM (Pluggable Authentication Modules) to authenticate MySQL users. PAM enables a system to use a standard interface to access various kinds of authentication methods, such as Unix passwords or an LDAP directory.

The PAM authentication plugin provides these capabilities:

  • External authentication: The plugin enables MySQL Server to accept connections from users defined outside the MySQL grant tables and that authenticate using methods supported by PAM.

  • Proxy user support: The plugin can return to MySQL a user name different from the login user, based on the groups the external user is in and the authentication string provided. This means that the plugin can return the MySQL user that defines the privileges the external PAM-authenticated user should have. For example, a PAM user named joe can connect and have the privileges of the MySQL user named developer.

The PAM authentication plugin has been tested on Linux and Mac OS X.

The PAM plugin uses the information passed to it by MySQL Server (such as user name, host name, password, and authentication string), plus whatever method is available for PAM lookup. The plugin checks the user credentials against PAM and returns 'Authentication succeeded, Username is user_name' or 'Authentication failed'.

The following table shows the plugin and library file names. The file name suffix might be different on your system. The file location must be the directory named by the plugin_dir system variable. For installation information, see Section 7.1.5.1, “Installing the PAM Authentication Plugin”.

Table 7.4 MySQL PAM Authentication Plugin

Server-side plugin nameauthentication_pam
Client-side plugin namemysql_clear_password
Library file nameauthentication_pam.so

The library file includes only the server-side plugin. The client-side plugin is built into the libmysqlclient client library. See Section 7.1.7, “The Cleartext Client-Side Authentication Plugin”.

The server-side PAM authentication plugin is included only in MySQL Enterprise Edition. It is not included in MySQL community distributions. The client-side clear-text plugin that communicates with the server-side plugin is built into the MySQL client library and is included in all distributions, including community distributions. This permits clients from any MySQL 5.6.10 or higher distribution to connect to a server that has the server-side plugin loaded.

For general information about pluggable authentication in MySQL, see Section 5.7, “Pluggable Authentication”. For proxy user information, see Section 5.8, “Proxy Users”.

7.1.5.1 Installing the PAM Authentication Plugin

The PAM authentication plugin must be located in the MySQL plugin directory (the directory named by the plugin_dir system variable). If necessary, set the value of plugin_dir at server startup to tell the server the plugin directory location.

To enable the plugin, start the server with the --plugin-load option. For example, put the following lines in your my.cnf file. If library files have a suffix different from .so on your system, substitute the correct suffix.

[mysqld]
plugin-load=authentication_pam.so

To verify plugin installation, examine the INFORMATION_SCHEMA.PLUGINS table or use the SHOW PLUGINS statement (see Obtaining Server Plugin Information). For example:

mysql> SELECT PLUGIN_NAME, PLUGIN_STATUS FROM INFORMATION_SCHEMA.PLUGINS
    -> WHERE PLUGIN_NAME LIKE 'authentication%';
+--------------------+---------------+
| PLUGIN_NAME        | PLUGIN_STATUS |
+--------------------+---------------+
| authentication_pam | ACTIVE        |
+--------------------+---------------+

To associate a MySQL account with the PAM plugin, use the plugin name authentication_pam in the IDENTIFIED WITH clause of CREATE USER or GRANT statement that creates the account.

7.1.5.2 Using the PAM Authentication Plugin

This section describes how to use the PAM authentication plugin to connect from MySQL client programs to the server. It is assumed that the server-side plugin is enabled, as described previously.

Note

The client-side plugin with which the PAM plugin communicates simply sends the password to the server in clear text so it can be passed to PAM. This may be a security problem in some configurations, but is necessary to use the server-side PAM library. To avoid problems if there is any possibility that the password would be intercepted, clients should connect to MySQL Server using a secure connection. See Section 7.1.7, “The Cleartext Client-Side Authentication Plugin”.

To refer to the PAM authentication plugin in the IDENTIFIED WITH clause of a CREATE USER or GRANT statement, use the name authentication_pam. For example:

CREATE USER user
  IDENTIFIED WITH authentication_pam
  AS 'authentication_string';

The authentication string specifies the following types of information:

  • PAM supports the notion of service name, which is a name that the system administrator can use to configure the authentication method for a particular application. There can be several such applications associated with a single database server instance, so the choice of service name is left to the SQL application developer. When you define an account that should authenticate using PAM, specify the service name in the authentication string.

  • PAM provides a way for a PAM module to return to the server a MySQL user name other than the login name supplied at login time. Use the authentication string to control the mapping between login name and MySQL user name. If you want to take advantage of proxy user capabilities, the authentication string must include this kind of mapping.

For example, if the service name is mysql and users in the root and users PAM groups should be mapped to the developer and data_entry MySQL users, respectively, use a statement like this:

CREATE USER user
  IDENTIFIED WITH authentication_pam
  AS 'mysql, root=developer, users=data_entry';

Authentication string syntax for the PAM authentication plugin follows these rules:

  • The string consists of a PAM service name, optionally followed by a group mapping list consisting of one or more keyword/value pairs each specifying a group name and a MySQL user name:

    pam_service_name[,group_name=mysql_user_name]...
    

    The plugin parses the authentication string on each login check. To minimize overhead, keep the string as short as possible.

  • Each group_name=mysql_user_name pair must be preceded by a comma.

  • Leading and trailing spaces not inside double quotation marks are ignored.

  • Unquoted pam_service_name, group_name, and mysql_user_name values can contain anything except equal sign, comma, or space.

  • If a pam_service_name, group_name, or mysql_user_name value is quoted with double quotation marks, everything between the quotation marks is part of the value. This is necessary, for example, if the value contains space characters. All characters are legal except double quotation mark and backslash (\). To include either character, escape it with a backslash.

If the plugin successfully authenticates a login name, it looks for a group mapping list in the authentication string and, if present, uses it to return a different user name to the MySQL server based on the groups the external user is a member of:

  • If the authentication string contains no group mapping list, the plugin returns the login name.

  • If the authentication string does contain a group mapping list, the plugin examines each group_name=mysql_user_name pair in the list from left to right and tries to find a match for the group_name value in a non-MySQL directory of the groups assigned to the authenticated user and returns mysql_user_name for the first match it finds. If the plugin finds no match for any group, it returns the login name. If the plugin is not capable of looking up a group in a directory, it ignores the group mapping list and returns the login name.

The following sections describe how to set up several authentication scenarios that use the PAM authentication plugin:

  • No proxy users. This uses PAM only to check login names and passwords. Every external user permitted to connect to MySQL Server should have a matching MySQL account that is defined to use external PAM authentication. (For a MySQL account of user_name@host_name to match the external user, user_name must be the login name and host_name must match the host from which the client connects.) Authentication can be performed by various PAM-supported methods. The discussion shows how to use traditional Unix passwords and LDAP.

    PAM authentication, when not done through proxy users or groups, requires the MySQL account to have the same user name as the Unix account. Because MySQL user names are limited to 16 characters (see Section 4.2, “Grant Tables”), this limits PAM nonproxy authentication to Unix accounts with names of at most 16 characters.

  • Proxy login only and group mapping. For this scenario, create one or a few MySQL accounts that define different sets of privileges. (Ideally, nobody should connect using those accounts directly.) Then define a default user authenticating through PAM that uses some mapping scheme (usually by the external groups the users are in) to map all the external logins to the few MySQL accounts holding the privilege sets. Any user that logs in is mapped to one of the MySQL accounts and uses its privileges. The discussion shows how to set this up using Unix passwords, but other PAM methods such as LDAP could be used instead.

Variations on these scenarios are possible. For example, you can permit some users to log in directly (without proxying) but require others to connect through proxy users.

The examples make the following assumptions. You might need to make some adjustments if your system is set up differently.

  • The PAM configuration directory is /etc/pam.d.

  • The PAM service name is mysql, which means that you must set up a PAM file named mysql in the PAM configuration directory (creating the file if it does not exist). If you use a service name different from mysql, the file name will be different and you must use a different name in the AS 'auth_string' clause of CREATE USER and GRANT statements.

  • The examples use a login name of antonio and password of verysecret. Change these to correspond to the users you want to authenticate.

The PAM authentication plugin checks at initialization time whether the AUTHENTICATION_PAM_LOG environment value is set in the server's startup environment. If so, the plugin enables logging of diagnostic messages to the standard output. Depending on how your server is started, the message might appear on the console or in the error log. These messages can be helpful for debugging PAM-related problems that occur when the plugin performs authentication. For more information, see Section 7.1.5.6, “PAM Authentication Plugin Debugging”.

7.1.5.3 Unix Password Authentication without Proxy Users

This authentication scenario uses PAM only to check Unix user login names and passwords. Every external user permitted to connect to MySQL Server should have a matching MySQL account that is defined to use external PAM authentication.

  1. Verify that Unix authentication in PAM permits you to log in as antonio with password verysecret.

  2. Set up PAM to authenticate the mysql service by creating a file named /etc/pam.d/mysql. The file contents are system dependent, so check existing login-related files in the /etc/pam.d directory to see what they look like. On Linux, the mysql file might look like this:

    #%PAM-1.0
    auth            include         password-auth
    account         include         password-auth
    

    For Gentoo Linux, use system-login rather than password-auth. For OS X, use login rather than password-auth.

    On Ubuntu and other Debian-based systems, use these file contents instead:

    @include common-auth
    @include common-account
    @include common-session-noninteractive
    
  3. Create a MySQL account with the same user name as the Unix login name and define it to authenticate using the PAM plugin:

    CREATE USER 'antonio'@'localhost'
      IDENTIFIED WITH authentication_pam AS 'mysql';
    GRANT ALL PRIVILEGES ON mydb.* TO 'antonio'@'localhost';
    
  4. Connect to the MySQL server using the mysql command-line client. For example:

    mysql --user=antonio --password=verysecret --enable-cleartext-plugin mydb
    

    The server should permit the connection and the following query should return output as shown:

    mysql> SELECT USER(), CURRENT_USER(), @@proxy_user;
    +-------------------+-------------------+--------------+
    | USER()            | CURRENT_USER()    | @@proxy_user |
    +-------------------+-------------------+--------------+
    | antonio@localhost | antonio@localhost | NULL         |
    +-------------------+-------------------+--------------+
    

    This demonstrates that antonio uses the privileges granted to the antonio MySQL account, and that no proxying has occurred.

7.1.5.4 LDAP Authentication without Proxy Users

This authentication scenario uses PAM only to check LDAP user login names and passwords. Every external user permitted to connect to MySQL Server should have a matching MySQL account that is defined to use external PAM authentication.

  1. Verify that LDAP authentication in PAM permits you to log in as antonio with password verysecret.

  2. Set up PAM to authenticate the mysql service through LDAP by creating a file named /etc/pam.d/mysql. The file contents are system dependent, so check existing login-related files in the /etc/pam.d directory to see what they look like. On Linux, the mysql file might look like this:

    #%PAM-1.0
    auth        required    pam_ldap.so
    account     required    pam_ldap.so
    

    If PAM object files have a suffix different from .so on your system, substitute the correct suffix.

    The PAM file might have a different format on some systems.

  3. MySQL account creation and connecting to the server is the same as previously described in Section 7.1.5.3, “Unix Password Authentication without Proxy Users”.

7.1.5.5 Unix Password Authentication with Proxy Users and Group Mapping

This authentication scheme uses proxying and group mapping to map users who connect to the MySQL server through PAM onto MySQL accounts that define different sets of privileges. Users do not connect directly through the accounts that define the privileges. Instead, they connect through a default proxy user authenticating through PAM that uses a mapping scheme to map all the external logins to the few MySQL accounts holding the privileges. Any user who connects is mapped to one of the MySQL accounts and uses its privileges.

The procedure shown here uses Unix password authentication. To use LDAP instead, see the early steps of Section 7.1.5.4, “LDAP Authentication without Proxy Users”.

  1. Verify that Unix authentication in PAM permits you to log in as antonio with password verysecret and that antonio is a member of the root or users group.

  2. Set up PAM to authenticate the mysql service. Put the following in /etc/pam.d/mysql:

    #%PAM-1.0
    auth            include         password-auth
    account         include         password-auth
    

    use system-login rather than password-auth. For OS X, use login rather than password-auth.

    The PAM file might have a different format on some systems. For example, on Ubuntu and other Debian-based systems, use these file contents instead:

    @include common-auth
    @include common-account
    @include common-session-noninteractive
    
  3. Create a default proxy user (''@'') that maps the external PAM users to the proxied accounts. It maps external users from the root PAM group to the developer MySQL account and the external users from the users PAM group to the data_entry MySQL account:

    CREATE USER ''@''
      IDENTIFIED WITH authentication_pam
      AS 'mysql, root=developer, users=data_entry';
    

    The mapping list following the service name is required when you set up proxy users. Otherwise, the plugin cannot tell how to map the name of PAM groups to the proper proxied user name.

    If your MySQL installation has anonymous users, they might conflict with the default proxy user. For more information about this problem, and ways of dealing with it, see Default Proxy User and Anonymous User Conflicts.

  4. Create the proxied accounts that will be used to access the databases:

    CREATE USER 'developer'@'localhost' IDENTIFIED BY 'very secret password';
    GRANT ALL PRIVILEGES ON mydevdb.* TO 'developer'@'localhost';
    CREATE USER 'data_entry'@'localhost' IDENTIFIED BY 'very secret password';
    GRANT ALL PRIVILEGES ON mydb.* TO 'data_entry'@'localhost'; 
    

    If you do not let anyone know the passwords for these accounts, other users cannot use them to connect directly to the MySQL server. Instead, it is expected that users will authenticate using PAM and that they will use the developer or data_entry account by proxy based on their PAM group.

  5. Grant the PROXY privilege to the proxy account for the proxied accounts:

    GRANT PROXY ON 'developer'@'localhost' TO ''@'';
    GRANT PROXY ON 'data_entry'@'localhost' TO ''@'';
    
  6. Connect to the MySQL server using the mysql command-line client. For example:

    mysql --user=antonio --password=verysecret --enable-cleartext-plugin mydb
    

    The server authenticates the connection using the ''@'' account. The privileges antonio will have depends on what PAM groups he is a member of. If antonio is a member of the root PAM group, the PAM plugin maps root to the developer MySQL user name and returns that name to the server. The server verifies that ''@'' has the PROXY privilege for developer and permits the connection. the following query should return output as shown:

    mysql> SELECT USER(), CURRENT_USER(), @@proxy_user;
    +-------------------+---------------------+--------------+
    | USER()            | CURRENT_USER()      | @@proxy_user |
    +-------------------+---------------------+--------------+
    | antonio@localhost | developer@localhost | ''@''        |
    +-------------------+---------------------+--------------+
    

    This demonstrates that antonio uses the privileges granted to the developer MySQL account, and that proxying occurred through the default proxy user account.

    If antonio is not a member of the root PAM group but is a member of the users group, a similar process occurs, but the plugin maps user group membership to the data_entry MySQL user name and returns that name to the server. In this case, antonio uses the privileges of the data_entry MySQL account:

    mysql> SELECT USER(), CURRENT_USER(), @@proxy_user;
    +-------------------+----------------------+--------------+
    | USER()            | CURRENT_USER()       | @@proxy_user |
    +-------------------+----------------------+--------------+
    | antonio@localhost | data_entry@localhost | ''@''        |
    +-------------------+----------------------+--------------+
    

7.1.5.6 PAM Authentication Plugin Debugging

The PAM authentication plugin checks at initialization time whether the AUTHENTICATION_PAM_LOG environment value is set (the value does not matter). If so, the plugin enables logging of diagnostic messages to the standard output. These messages may be helpful for debugging PAM-related problems that occur when the plugin performs authentication.

Some messages include reference to PAM plugin source files and line numbers, which enables plugin actions to be tied more closely to the location in the code where they occur.

The following transcript demonstrates the kind of information produced by enabling logging. It resulted from a successful proxy authentication attempt.

entering auth_pam_server
entering auth_pam_next_token
auth_pam_next_token:reading at [cups,admin=writer,everyone=reader], sep=[,]
auth_pam_next_token:state=PRESPACE, ptr=[cups,admin=writer,everyone=reader],
out=[]
auth_pam_next_token:state=IDENT, ptr=[cups,admin=writer,everyone=reader],
out=[]
auth_pam_next_token:state=AFTERSPACE, ptr=[,admin=writer,everyone=reader],
out=[cups]
auth_pam_next_token:state=DELIMITER, ptr=[,admin=writer,everyone=reader],
out=[cups]
auth_pam_next_token:state=DONE, ptr=[,admin=writer,everyone=reader],
out=[cups]
leaving auth_pam_next_token on
/Users/gkodinov/mysql/work/x-5.5.16-release-basket/release/plugin/pam-authentication-plugin/src/parser.c:191
auth_pam_server:password 12345qq received
auth_pam_server:pam_start rc=0
auth_pam_server:pam_set_item(PAM_RUSER,gkodinov) rc=0
auth_pam_server:pam_set_item(PAM_RHOST,localhost) rc=0
entering auth_pam_server_conv
auth_pam_server_conv:PAM_PROMPT_ECHO_OFF [Password:] received
leaving auth_pam_server_conv on
/Users/gkodinov/mysql/work/x-5.5.16-release-basket/release/plugin/pam-authentication-plugin/src/authentication_pam.c:257
auth_pam_server:pam_authenticate rc=0
auth_pam_server:pam_acct_mgmt rc=0
auth_pam_server:pam_setcred(PAM_ESTABLISH_CRED) rc=0
auth_pam_server:pam_get_item rc=0
auth_pam_server:pam_setcred(PAM_DELETE_CRED) rc=0
entering auth_pam_map_groups
entering auth_pam_walk_namevalue_list
auth_pam_walk_namevalue_list:reading at: [admin=writer,everyone=reader]
entering auth_pam_next_token
auth_pam_next_token:reading at [admin=writer,everyone=reader], sep=[=]
auth_pam_next_token:state=PRESPACE, ptr=[admin=writer,everyone=reader], out=[]
auth_pam_next_token:state=IDENT, ptr=[admin=writer,everyone=reader], out=[]
auth_pam_next_token:state=AFTERSPACE, ptr=[=writer,everyone=reader],
out=[admin]
auth_pam_next_token:state=DELIMITER, ptr=[=writer,everyone=reader],
out=[admin]
auth_pam_next_token:state=DONE, ptr=[=writer,everyone=reader], out=[admin]
leaving auth_pam_next_token on
/Users/gkodinov/mysql/work/x-5.5.16-release-basket/release/plugin/pam-authentication-plugin/src/parser.c:191
auth_pam_walk_namevalue_list:name=[admin]
entering auth_pam_next_token
auth_pam_next_token:reading at [writer,everyone=reader], sep=[,]
auth_pam_next_token:state=PRESPACE, ptr=[writer,everyone=reader], out=[]
auth_pam_next_token:state=IDENT, ptr=[writer,everyone=reader], out=[]
auth_pam_next_token:state=AFTERSPACE, ptr=[,everyone=reader], out=[writer]
auth_pam_next_token:state=DELIMITER, ptr=[,everyone=reader], out=[writer]
auth_pam_next_token:state=DONE, ptr=[,everyone=reader], out=[writer]
leaving auth_pam_next_token on
/Users/gkodinov/mysql/work/x-5.5.16-release-basket/release/plugin/pam-authentication-plugin/src/parser.c:191
walk, &error_namevalue_list:value=[writer]
entering auth_pam_map_group_to_user
auth_pam_map_group_to_user:pam_user=gkodinov, name=admin, value=writer
examining member root
examining member gkodinov
substitution was made to mysql user writer
leaving auth_pam_map_group_to_user on
/Users/gkodinov/mysql/work/x-5.5.16-release-basket/release/plugin/pam-authentication-plugin/src/authentication_pam.c:118
auth_pam_walk_namevalue_list:found mapping
leaving auth_pam_walk_namevalue_list on
/Users/gkodinov/mysql/work/x-5.5.16-release-basket/release/plugin/pam-authentication-plugin/src/parser.c:270
auth_pam_walk_namevalue_list returned 0
leaving auth_pam_map_groups on
/Users/gkodinov/mysql/work/x-5.5.16-release-basket/release/plugin/pam-authentication-plugin/src/authentication_pam.c:171
auth_pam_server:authenticated_as=writer
auth_pam_server: rc=0
leaving auth_pam_server on
/Users/gkodinov/mysql/work/x-5.5.16-release-basket/release/plugin/pam-authentication-plugin/src/authentication_pam.c:429

7.1.6 The Windows Native Authentication Plugin

Note

The Windows authentication plugin is an extension included in MySQL Enterprise Edition, a commercial product. To learn more about commercial products, see http://www.mysql.com/products/.

As of MySQL 5.6.10, MySQL Enterprise Edition for Windows includes an authentication plugin that performs external authentication on Windows, enabling MySQL Server to use native Windows services to authenticate client connections. Users who have logged in to Windows can connect from MySQL client programs to the server based on the information in their environment without specifying an additional password.

The client and server exchange data packets in the authentication handshake. As a result of this exchange, the server creates a security context object that represents the identity of the client in the Windows OS. This identity includes the name of the client account. The Windows authentication plugin uses the identity of the client to check whether it is a given account or a member of a group. By default, negotiation uses Kerberos to authenticate, then NTLM if Kerberos is unavailable.

The Windows authentication plugin provides these capabilities:

  • External authentication: The plugin enables MySQL Server to accept connections from users defined outside the MySQL grant tables.

  • Proxy user support: The plugin can return to MySQL a user name different from the client user. This means that the plugin can return the MySQL user that defines the privileges the external Windows-authenticated user should have. For example, a Windows user named joe can connect and have the privileges of the MySQL user named developer.

The following table shows the plugin and library file names. The file location must be the directory named by the plugin_dir system variable. For installation information, see Section 7.1.6.1, “Installing the Windows Authentication Plugin”.

Table 7.5 MySQL Windows Authentication Plugin

Server-side plugin nameauthentication_windows
Client-side plugin nameauthentication_windows_client
Library file nameauthentication_windows.dll

The library file includes only the server-side plugin. The client-side plugin is built into the libmysqlclient client library.

The server-side Windows authentication plugin is included only in MySQL Enterprise Edition. It is not included in MySQL community distributions. The client-side plugin is included in all distributions, including community distributions. This permits clients from any distribution to connect to a server that has the server-side plugin loaded.

The Windows authentication plugin is supported on any version of Windows supported by MySQL 5.6 (see http://www.mysql.com/support/supportedplatforms/database.html). It requires MySQL Server 5.6.10 or higher.

For general information about pluggable authentication in MySQL, see Section 5.7, “Pluggable Authentication”. For proxy user information, see Section 5.8, “Proxy Users”.

7.1.6.1 Installing the Windows Authentication Plugin

This section describes how to install the Windows authentication plugin. For general information about installing plugins, see Installing and Uninstalling Plugins.

To be usable by the server, the plugin library file must be located in the MySQL plugin directory (the directory named by the plugin_dir system variable). If necessary, set the value of plugin_dir at server startup to tell the server the plugin directory location.

To enable the plugin, start the server with the --plugin-load option. For example, put these lines in your my.ini file:

[mysqld]
plugin-load=authentication_windows.dll

To verify plugin installation, examine the INFORMATION_SCHEMA.PLUGINS table or use the SHOW PLUGINS statement (see Obtaining Server Plugin Information). For example:

mysql> SELECT PLUGIN_NAME, PLUGIN_STATUS FROM INFORMATION_SCHEMA.PLUGINS
    -> WHERE PLUGIN_NAME LIKE 'authentication%';
+------------------------+---------------+
| PLUGIN_NAME            | PLUGIN_STATUS |
+------------------------+---------------+
| authentication_windows | ACTIVE        |
+------------------------+---------------+

To associate a MySQL account with the Windows authentication plugin, use the plugin name authentication_windows in the IDENTIFIED WITH clause of CREATE USER or GRANT statement that creates the account.

7.1.6.2 Using the Windows Authentication Plugin

The Windows authentication plugin supports the use of MySQL accounts such that users who have logged in to Windows can connect to the MySQL server without having to specify an additional password. It is assumed that the server-side plugin is enabled, as described previously. Once the DBA has enabled the server-side plugin and set up accounts to use it, clients can connect using those accounts with no other setup required on their part.

To refer to the Windows authentication plugin in the IDENTIFIED WITH clause of a CREATE USER or GRANT statement, use the name authentication_windows. Suppose that the Windows users Rafal and Tasha should be permitted to connect to MySQL, as well as any users in the Administrators or Power Users group. To set this up, create a MySQL account named sql_admin that uses the Windows plugin for authentication:

CREATE USER sql_admin
  IDENTIFIED WITH authentication_windows
  AS 'Rafal, Tasha, Administrators, "Power Users"';

The plugin name is authentication_windows. The string following the AS keyword is the authentication string. It specifies that the Windows users named Rafal or Tasha are permitted to authenticate to the server as the MySQL user sql_admin, as are any Windows users in the Administrators or Power Users group. The latter group name contains a space, so it must be quoted with double quote characters.

After you create the sql_admin account, a user who has logged in to Windows can attempt to connect to the server using that account:

C:\> mysql --user=sql_admin

No password is required here. The authentication_windows plugin uses the Windows security API to check which Windows user is connecting. If that user is named Rafal or Tasha, or is in the Administrators or Power Users group, the server grants access and the client is authenticated as sql_admin and has whatever privileges are granted to the sql_admin account. Otherwise, the server denies access.

Authentication string syntax for the Windows authentication plugin follows these rules:

  • The string consists of one or more user mappings separated by commas.

  • Each user mapping associates a Windows user or group name with a MySQL user name:

    win_user_or_group_name=mysql_user_name
    win_user_or_group_name
    

    For the latter syntax, with no mysql_user_name value given, the implicit value is the MySQL user created by the CREATE USER statement. Thus, these statements are equivalent:

    CREATE USER sql_admin
      IDENTIFIED WITH authentication_windows
      AS 'Rafal, Tasha, Administrators, "Power Users"';
    CREATE USER sql_admin
      IDENTIFIED WITH authentication_windows
      AS 'Rafal=sql_admin, Tasha=sql_admin, Administrators=sql_admin,
          "Power Users"=sql_admin';
    
  • Each backslash ('\') in a value must be doubled because backslash is the escape character in MySQL strings.

  • Leading and trailing spaces not inside double quotation marks are ignored.

  • Unquoted win_user_or_group_name and mysql_user_name values can contain anything except equal sign, comma, or space.

  • If a win_user_or_group_name and or mysql_user_name value is quoted with double quotation marks, everything between the quotation marks is part of the value. This is necessary, for example, if the name contains space characters. All characters within double quotes are legal except double quotation mark and backslash. To include either character, escape it with a backslash.

  • win_user_or_group_name values use conventional syntax for Windows principals, either local or in a domain. Examples (note the doubling of backslashes):

    domain\\user
    .\\user
    domain\\group
    .\\group
    BUILTIN\\WellKnownGroup
    

When invoked by the server to authenticate a client, the plugin scans the authentication string left to right for a user or group match to the Windows user. If there is a match, the plugin returns the corresponding mysql_user_name to the MySQL server. If there is no match, authentication fails.

A user name match takes preference over a group name match. Suppose that the Windows user named win_user is a member of win_group and the authentication string looks like this:

'win_group = sql_user1, win_user = sql_user2'

When win_user connects to the MySQL server, there is a match both to win_group and to win_user. The plugin authenticates the user as sql_user2 because the more-specific user match takes precedence over the group match, even though the group is listed first in the authentication string.

Windows authentication always works for connections from the same computer on which the server is running. For cross-computer connections, both computers must be registered with Windows Active Directory. If they are in the same Windows domain, it is unnecessary to specify a domain name. It is also possible to permit connections from a different domain, as in this example:

CREATE USER sql_accounting
  IDENTIFIED WITH authentication_windows
  AS 'SomeDomain\\Accounting';

Here SomeDomain is the name of the other domain. The backslash character is doubled because it is the MySQL escape character within strings.

MySQL supports the concept of proxy users whereby a client can connect and authenticate to the MySQL server using one account but while connected has the privileges of another account (see Section 5.8, “Proxy Users”). Suppose that you want Windows users to connect using a single user name but be mapped based on their Windows user and group names onto specific MySQL accounts as follows:

  • The local_user and MyDomain\domain_user local and domain Windows users should map to the local_wlad MySQL account.

  • Users in the MyDomain\Developers domain group should map to the local_dev MySQL account.

  • Local machine administrators should map to the local_admin MySQL account.

To set this up, create a proxy account for Windows users to connect to, and configure this account so that users and groups map to the appropriate MySQL accounts (local_wlad, local_dev, local_admin). In addition, grant the MySQL accounts the privileges appropriate to the operations they need to perform. The following instructions use win_proxy as the proxy account, and local_wlad, local_dev, and local_admin as the proxied accounts.

  1. Create the proxy MySQL account:

    CREATE USER win_proxy
      IDENTIFIED WITH  authentication_windows
      AS 'local_user = local_wlad,
          MyDomain\\domain_user = local_wlad,
          MyDomain\\Developers = local_dev,
          BUILTIN\\Administrators = local_admin';
    
  2. For proxying to work, the proxied accounts must exist, so create them:

    CREATE USER local_wlad IDENTIFIED BY 'wlad_pass';
    CREATE USER local_dev IDENTIFIED BY 'dev_pass';
    CREATE USER local_admin IDENTIFIED BY  'admin_pass';
    

    If you do not let anyone know the passwords for these accounts, other users cannot use them to connect directly to the MySQL server.

    You should also issue GRANT statements (not shown) that grant each proxied account the privileges it needs.

  3. The proxy account must have the PROXY privilege for each of the proxied accounts:

    GRANT PROXY ON local_wlad TO win_proxy;
    GRANT PROXY ON local_dev TO win_proxy;
    GRANT PROXY ON local_admin TO win_proxy;
    

Now the Windows users local_user and MyDomain\domain_user can connect to the MySQL server as win_proxy and when authenticated have the privileges of the account given in the authentication string—in this case, local_wlad. A user in the MyDomain\Developers group who connects as win_proxy has the privileges of the local_dev account. A user in the BUILTIN\Administrators group has the privileges of the local_admin account.

To configure authentication so that all Windows users who do not have their own MySQL account go through a proxy account, substitute the default proxy user (''@'') for win_proxy in the preceding instructions. For information about the default proxy user, see Section 5.8, “Proxy Users”.

If your MySQL installation has anonymous users, they might conflict with the default proxy user. For more information about this problem, and ways of dealing with it, see Default Proxy User and Anonymous User Conflicts.

To use the Windows authentication plugin with Connector/Net connection strings in Connection/Net 6.4.4 and higher, see Using the Windows Native Authentication Plugin.

Additional control over the Windows authentication plugin is provided by the authentication_windows_use_principal_name and authentication_windows_log_level system variables. See Server System Variables.

7.1.7 The Cleartext Client-Side Authentication Plugin

As of MySQL 5.6.2, a client-side authentication plugin is available that sends the password to the server without hashing or encryption. This plugin is built into the MySQL client library.

The following table shows the plugin name.

Table 7.6 MySQL Cleartext Authentication Plugin

Server-side plugin nameNone, see discussion
Client-side plugin namemysql_clear_password
Library file nameNone (plugin is built in)

With native MySQL authentication, the client performs one-way hashing on the password before sending it to the server. This enables the client to avoid sending the password in clear text. See Section 2.2.4, “Password Hashing in MySQL”. However, because the hash algorithm is one way, the original password cannot be recovered on the server side.

One-way hashing cannot be done for authentication schemes that require the server to receive the password as entered on the client side. In such cases, the mysql_clear_password client-side plugin can be used to send the password to the server in clear text. There is no corresponding server-side plugin. Rather, the client-side plugin can be used by any server-side plugin that needs a clear text password. (The PAM authentication plugin is one such; see Section 7.1.5, “The PAM Authentication Plugin”.)

For general information about pluggable authentication in MySQL, see Section 5.7, “Pluggable Authentication”.

Note

Sending passwords in clear text may be a security problem in some configurations. To avoid problems if there is any possibility that the password would be intercepted, clients should connect to MySQL Server using a method that protects the password. Possibilities include SSL (see Chapter 6, Using Secure Connections), IPsec, or a private network.

As of MySQL 5.6.7, to make inadvertent use of this plugin less likely, it is required that clients explicitly enable it. This can be done several ways:

  • Set the LIBMYSQL_ENABLE_CLEARTEXT_PLUGIN environment variable to a value that begins with 1, Y, or y. This enables the plugin for all client connections.

  • The mysql, mysqladmin, and mysqlslap client programs support an --enable-cleartext-plugin option that enables the plugin on a per-invocation basis.

  • The mysql_options() C API function supports a MYSQL_ENABLE_CLEARTEXT_PLUGIN option that enables the plugin on a per-connection basis. Also, any program that uses libmysqlclient and reads option files can enable the plugin by including an enable-cleartext-plugin option in an option group read by the client library.

7.1.8 The Socket Peer-Credential Authentication Plugin

As of MySQL 5.6.2, a server-side authentication plugin is available that authenticates clients that connect from the local host through the Unix socket file.

The source code for this plugin can be examined as a relatively simple example demonstrating how to write a loadable authentication plugin.

The following table shows the plugin and library file names. The file name suffix might differ on your system. The file location is the directory named by the plugin_dir system variable. For installation information, see Section 5.7, “Pluggable Authentication”.

Table 7.7 MySQL Socket Peer-Credential Authentication Plugin

Server-side plugin nameauth_socket
Client-side plugin nameNone, see discussion
Library file nameauth_socket.so

The auth_socket authentication plugin authenticates clients that connect from the local host through the Unix socket file. The plugin uses the SO_PEERCRED socket option to obtain information about the user running the client program. Thus, the plugin can be built only on systems that support the SO_PEERCRED option, such as Linux.

The plugin checks whether the user name matches the MySQL user name specified by the client program to the server, and permits the connection only if the names match.

Suppose that a MySQL account is created for a user named valerie who is to be authenticated by the auth_socket plugin for connections from the local host through the socket file:

CREATE USER 'valerie'@'localhost' IDENTIFIED WITH auth_socket;

If a user on the local host with a login name of stefanie invokes mysql with the option --user=valerie to connect through the socket file, the server uses auth_socket to authenticate the client. The plugin determines that the --user option value (valerie) differs from the client user's name (stephanie) and refuses the connection. If a user named valerie tries the same thing, the plugin finds that the user name and the MySQL user name are both valerie and permits the connection. However, the plugin refuses the connection even for valerie if the connection is made using a different protocol, such as TCP/IP.

For general information about pluggable authentication in MySQL, see Section 5.7, “Pluggable Authentication”.

7.1.9 The Test Authentication Plugin

MySQL includes a test plugin that authenticates using MySQL native authentication, but is a loadable plugin (not built in) and must be installed prior to use. It can authenticate against either normal or older (shorter) password hash values.

This plugin is intended for testing and development purposes, and not for use in production environments. The test plugin source code is separate from the server source, unlike the built-in native plugin, so it can be examined as a relatively simple example demonstrating how to write a loadable authentication plugin.

The following table shows the plugin and library file names. The file name suffix might differ on your system. The file location is the directory named by the plugin_dir system variable. For installation information, see Section 5.7, “Pluggable Authentication”.

Table 7.8 MySQL Test Authentication Plugin

Server-side plugin nametest_plugin_server
Client-side plugin nameauth_test_plugin
Library file nameauth_test_plugin.so

Because the test plugin authenticates the same way as native MySQL authentication, provide the usual --user and --password options that you normally use for accounts that use native authentication when you connect to the server. For example:

shell> mysql --user=your_name --password=your_pass

For general information about pluggable authentication in MySQL, see Section 5.7, “Pluggable Authentication”.

7.2 The Password Validation Plugin

The validate_password plugin (available as of MySQL 5.6.6) serves to test passwords and improve security. The plugin exposes a set of system variables that enable you to define password policy.

This plugin implements two capabilities:

  • In statements that assign a password supplied as a cleartext value, the plugin checks the password against the current password policy and rejects it if it is weak (the statement returns an ER_NOT_VALID_PASSWORD error). This affects the CREATE USER, GRANT, and SET PASSWORD statements. Passwords given as arguments to the PASSWORD() and OLD_PASSWORD() functions are checked as well.

  • The VALIDATE_PASSWORD_STRENGTH() SQL function assesses the strength of potential passwords. The function takes a password argument and returns an integer from 0 (weak) to 100 (strong).

For example, the cleartext password in the following statement is checked. Under the default password policy, which requires passwords to be at least 8 characters long, the password is weak and the statement produces an error:

mysql> SET PASSWORD = PASSWORD('abc');
ERROR 1819 (HY000): Your password does not satisfy the current
policy requirements

Passwords specified as hashed values are not checked because the original password value is not available:

mysql> SET PASSWORD = '*0D3CED9BEC10A777AEC23CCC353A8C08A633045E';
Query OK, 0 rows affected (0.01 sec)

System variables having names of the form validate_password_xxx represent the parameters that control password policy. To configure password checking, modify these variables; see Section 7.2.2, “Password Validation Plugin Options and Variables”.

If the validate_password plugin is not installed, the validate_password_xxx system variables are not available, passwords in statements are not checked, and the VALIDATE_PASSWORD_STRENGTH() function always returns 0. For example, without the plugin installed, accounts can be assigned passwords shorter than 8 characters.

Assuming that the validate_password plugin is installed, it implements three levels of password checking: LOW, MEDIUM, and STRONG. The default is MEDIUM; to change this, modify the value of validate_password_policy. The policies implement increasingly strict password tests. The following descriptions refer to default parameter values, which can be modified by changing the appropriate system variables.

  • LOW policy tests password length only. Passwords must be at least 8 characters long.

  • MEDIUM policy adds the conditions that passwords must contain at least 1 numeric character, 1 lowercase and uppercase character, and 1 special (nonalphanumeric) character.

  • STRONG policy adds the condition that password substrings of length 4 or longer must not match words in the dictionary file, if one has been specified.

7.2.1 Password Validation Plugin Installation

This section describes how to install the validate_password password-validation plugin. For general information about installing plugins, see Installing and Uninstalling Plugins.

To be usable by the server, the plugin library file must be located in the MySQL plugin directory (the directory named by the plugin_dir system variable). If necessary, set the value of plugin_dir at server startup to tell the server the plugin directory location.

The plugin library file base name is validate_password. The file name suffix differs per platform (for example, .so for Unix and Unix-like systems, .dll for Windows).

To load the plugin at server startup, use the --plugin-load option to name the library file that contains the plugin. With this plugin-loading method, the option must be given each time the server starts. For example, put these lines in your my.cnf file (adjust the .so suffix for your platform as necessary):

[mysqld]
plugin-load=validate_password.so

Alternatively, to register the plugin at runtime, use this statement (adjust the extension as necessary):

INSTALL PLUGIN validate_password SONAME 'validate_password.so';

INSTALL PLUGIN loads the plugin, and also registers it in the mysql.plugins table to cause the plugin to be loaded for each subsequent normal server startup.

To verify plugin installation, examine the INFORMATION_SCHEMA.PLUGINS table or use the SHOW PLUGINS statement (see Obtaining Server Plugin Information). For example:

mysql> SELECT PLUGIN_NAME, PLUGIN_STATUS FROM INFORMATION_SCHEMA.PLUGINS
    -> WHERE PLUGIN_NAME LIKE 'validate%';
+-------------------+---------------+
| PLUGIN_NAME       | PLUGIN_STATUS |
+-------------------+---------------+
| validate_password | ACTIVE        |
+-------------------+---------------+

If the plugin has been previously registered with INSTALL PLUGIN or is loaded with --plugin-load, you can use the --validate-password option at server startup to control plugin activation. For example, to load the plugin at startup and prevent it from being removed at runtime, use these options:

[mysqld]
plugin-load=validate_password.so
validate-password=FORCE_PLUS_PERMANENT

If it is desired to prevent the server from running without the password-validation plugin, use --validate-password with a value of FORCE or FORCE_PLUS_PERMANENT to force server startup to fail if the plugin does not initialize successfully.

7.2.2 Password Validation Plugin Options and Variables

To control the activation of the validate_password plugin, use this option:

If the validate_password plugin is enabled, it exposes several system variables representing the parameters that control password checking:

mysql> SHOW VARIABLES LIKE 'validate_password%';
+--------------------------------------+--------+
| Variable_name                        | Value  |
+--------------------------------------+--------+
| validate_password_dictionary_file    |        |
| validate_password_length             | 8      |
| validate_password_mixed_case_count   | 1      |
| validate_password_number_count       | 1      |
| validate_password_policy             | MEDIUM |
| validate_password_special_char_count | 1      |
+--------------------------------------+--------+

To change how passwords are checked, you can set these system variables at server startup or at runtime. The following list describes the meaning of each variable.

  • validate_password_dictionary_file

    Introduced5.6.6
    System Variable (<= 5.6.25)Namevalidate_password_dictionary_file
    Variable ScopeGlobal
    Dynamic VariableNo
    System VariableNamevalidate_password_dictionary_file
    Variable ScopeGlobal
    Dynamic VariableNo
    System Variable (>= 5.6.26)Namevalidate_password_dictionary_file
    Variable ScopeGlobal
    Dynamic VariableYes
    Permitted ValuesTypefile name

    The path name of the dictionary file used by the validate_password plugin for checking passwords. This variable is unavailable unless that plugin is installed.

    By default, this variable has an empty value and dictionary checks are not performed. To enable dictionary checks, you must set this variable to a nonempty value. If the file is named as a relative path, it is interpreted relative to the server data directory. Its contents should be lowercase, one word per line. Contents are treated as having a character set of utf8. The maximum permitted file size is 1MB.

    For the dictionary file to be used during password checking, the password policy must be set to 2 (STRONG); see the description of the validate_password_policy system variable. Assuming that is true, each substring of the password of length 4 up to 100 is compared to the words in the dictionary file. Any match causes the password to be rejected. Comparisons are not case sensitive.

    For VALIDATE_PASSWORD_STRENGTH() the password is checked against all policies, including STRONG, so the strength assessment includes the dictionary check regardless of the validate_password_policy value.

    Before MySQL 5.6.26, changes to the dictionary file while the server is running require a restart for the server to recognize the changes. As of MySQL 5.6.26, validate_password_dictionary_file can be set at runtime and assigning a value causes the named file to be read without a restart.

  • validate_password_length

    Introduced5.6.6
    System VariableNamevalidate_password_length
    Variable ScopeGlobal
    Dynamic VariableYes
    Permitted ValuesTypeinteger
    Default8
    Min Value0

    The minimum number of characters that passwords checked by the validate_password plugin must have. This variable is unavailable unless that plugin is installed.

    The validate_password_length minimum value is a function of several other related system variables. As of MySQL 5.6.10, the server will not set the value less than the value of this expression:

    validate_password_number_count
    + validate_password_special_char_count
    + (2 * validate_password_mixed_case_count)
    

    If the validate_password plugin adjusts the value of validate_password_length due to the preceding constraint, it writes a message to the error log.

  • validate_password_mixed_case_count

    Introduced5.6.6
    System VariableNamevalidate_password_mixed_case_count
    Variable ScopeGlobal
    Dynamic VariableYes
    Permitted ValuesTypeinteger
    Default1
    Min Value0

    The minimum number of lowercase and uppercase characters that passwords checked by the validate_password plugin must have if the password policy is MEDIUM or stronger. This variable is unavailable unless that plugin is installed.

  • validate_password_number_count

    Introduced5.6.6
    System VariableNamevalidate_password_number_count
    Variable ScopeGlobal
    Dynamic VariableYes
    Permitted ValuesTypeinteger
    Default1
    Min Value0

    The minimum number of numeric (digit) characters that passwords checked by the validate_password plugin must have if the password policy is MEDIUM or stronger. This variable is unavailable unless that plugin is installed.

  • validate_password_policy

    Introduced5.6.6
    System VariableNamevalidate_password_policy
    Variable ScopeGlobal
    Dynamic VariableYes
    Permitted ValuesTypeenumeration
    Default1
    Valid Values0
    1
    2

    The password policy enforced by the validate_password plugin. This variable is unavailable unless that plugin is installed.

    The validate_password_policy value can be specified using numeric values 0, 1, 2, or the corresponding symbolic values LOW, MEDIUM, STRONG. The following table describes the tests performed for each policy. For the length test, the required length is the value of the validate_password_length system variable. Similarly, the required values for the other tests are given by other validate_password_xxx variables.

    PolicyTests Performed
    0 or LOWLength
    1 or MEDIUMLength; numeric, lowercase/uppercase, and special characters
    2 or STRONGLength; numeric, lowercase/uppercase, and special characters; dictionary file
    Note

    Before MySQL 5.6.10, validate_password_policy was named validate_password_policy_number.

  • validate_password_special_char_count

    Introduced5.6.6
    System VariableNamevalidate_password_special_char_count
    Variable ScopeGlobal
    Dynamic VariableYes
    Permitted ValuesTypeinteger
    Default1
    Min Value0

    The minimum number of nonalphanumeric characters that passwords checked by the validate_password plugin must have if the password policy is MEDIUM or stronger. This variable is unavailable unless that plugin is installed.

If the validate_password plugin is enabled, it exposes status variables that provide operational information:

mysql> SHOW STATUS LIKE 'validate_password%';
+-----------------------------------------------+---------------------+
| Variable_name                                 | Value               |
+-----------------------------------------------+---------------------+
| validate_password_dictionary_file_last_parsed | 2015-06-29 11:08:51 |
| validate_password_dictionary_file_words_count | 1902                |
+-----------------------------------------------+---------------------+

The following list describes the meaning of each status variable.

7.3 MySQL Enterprise Audit

Note

MySQL Enterprise Audit is an extension included in MySQL Enterprise Edition, a commercial product. To learn more about commercial products, see http://www.mysql.com/products/.

As of MySQL 5.6.10, MySQL Enterprise Edition includes MySQL Enterprise Audit, implemented using a server plugin named audit_log. MySQL Enterprise Audit uses the open MySQL Audit API to enable standard, policy-based monitoring and logging of connection and query activity executed on specific MySQL servers. Designed to meet the Oracle audit specification, MySQL Enterprise Audit provides an out of box, easy to use auditing and compliance solution for applications that are governed by both internal and external regulatory guidelines.

When installed, the audit plugin enables MySQL Server to produce a log file containing an audit record of server activity. The log contents include when clients connect and disconnect, and what actions they perform while connected, such as which databases and tables they access.

After you install the plugin (see Section 7.3.1, “Installing MySQL Enterprise Audit”), it writes an audit log file. By default, the file is named audit.log in the server data directory. To change the name of the file, set the audit_log_file system variable at server startup.

Audit log file contents are not encrypted. See Section 7.3.2, “MySQL Enterprise Audit Security Considerations”.

The audit log file is written in XML, with auditable events encoded as <AUDIT_RECORD> elements. To select the file format, set the audit_log_format system variable at server startup. For details on file format and contents, see Section 7.3.3, “The Audit Log File”.

For more information about controlling how logging occurs, see Section 7.3.4, “Audit Log Logging Control”. To perform filtering of audited events, see Section 7.3.5, “Audit Log Filtering”. For descriptions of the parameters used to configure the audit log plugin, see Section 7.3.6.2, “Audit Log Options and System Variables”.

If the audit_log plugin is enabled, the Performance Schema (see MySQL Performance Schema) has instrumentation for the audit log plugin. To identify the relevant instruments, use this query:

SELECT NAME FROM performance_schema.setup_instruments
WHERE NAME LIKE '%/alog/%';

Changes from Older MySQL Enterprise Audit Versions

Several changes were made to the audit log plugin in MySQL 5.6.14 for better compatibility with Oracle Audit Vault.

A new audit log file format was implemented. It is possible to select either the old or new format using the audit_log_format system variable, which has permitted values of OLD and NEW (default OLD). The two formats differ as follows:

  • Information within <AUDIT_RECORD> elements written in the old format using attributes is written in the new format using subelements.

  • The new format includes more information in <AUDIT_RECORD> elements. Every element includes a RECORD_ID value providing a unique identifier. The TIMESTAMP value includes time zone information. Query records include HOST, IP, OS_LOGIN, and USER information, as well as COMMAND_CLASS and STATUS_CODE values.

Example of old <AUDIT_RECORD> format:

<AUDIT_RECORD
 TIMESTAMP="2013-09-15T15:27:27"
 NAME="Query"
 CONNECTION_ID="3"
 STATUS="0"
 SQLTEXT="SELECT 1"
/>

Example of new <AUDIT_RECORD> format:

<AUDIT_RECORD>
 <TIMESTAMP>2013-09-15T15:27:27 UTC</TIMESTAMP>
 <RECORD_ID>3998_2013-09-15T15:27:27</RECORD_ID>
 <NAME>Query</NAME>
 <CONNECTION_ID>3</CONNECTION_ID>
 <STATUS>0</STATUS>
 <STATUS_CODE>0</STATUS_CODE>
 <USER>root[root] @ localhost [127.0.0.1]</USER>
 <OS_LOGIN></OS_LOGIN>
 <HOST>localhost</HOST>
 <IP>127.0.0.1</IP>
 <COMMAND_CLASS>select</COMMAND_CLASS>
 <SQLTEXT>SELECT 1</SQLTEXT>
</AUDIT_RECORD>

When the audit log plugin rotates the audit log file, it uses a different file name format. For a log file named audit.log, the plugin previously renamed the file to audit.log.TIMESTAMP. The plugin now renames the file to audit.log.TIMESTAMP.xml to indicate that it is an XML file.

If you change the value of audit_log_format, use this procedure to avoid writing log entries in one format to an existing log file that contains entries in a different format:

  1. Stop the server.

  2. Rename the current audit log file manually.

  3. Restart the server with the new value of audit_log_format. The audit log plugin creates a new log file, which will contain log entries in the selected format.

The API for writing audit plugins has also changed. The mysql_event_general structure has new members to represent client host name and IP address, command class, and external user. For more information, see Writing Audit Plugins.

7.3.1 Installing MySQL Enterprise Audit

This section describes how to install MySQL Enterprise Audit, which is implemented using the audit_log plugin. For general information about installing plugins, see Installing and Uninstalling Plugins.

Note

If installed, the audit_log plugin involves some minimal overhead even when disabled. To avoid this overhead, do not install MySQL Enterprise Audit unless you plan to use it.

To be usable by the server, the plugin library file must be located in the MySQL plugin directory (the directory named by the plugin_dir system variable). If necessary, set the value of plugin_dir at server startup to tell the server the plugin directory location.

The plugin library file base name is audit_log. The file name suffix differs per platform (for example, .so for Unix and Unix-like systems, .dll for Windows).

To load the plugin at server startup, use the --plugin-load option to name the library file that contains the plugin. With this plugin-loading method, the option must be given each time the server starts. For example, put the following lines in your my.cnf file (adjust the .so suffix for your platform as necessary):

[mysqld]
plugin-load=audit_log.so

Alternatively, to register the plugin at runtime, use this statement (adjust the suffix as necessary):

INSTALL PLUGIN audit_log SONAME 'audit_log.so';

INSTALL PLUGIN loads the plugin, and also registers it in the mysql.plugins table to cause the plugin to be loaded for each subsequent normal server startup.

To verify plugin installation, examine the INFORMATION_SCHEMA.PLUGINS table or use the SHOW PLUGINS statement (see Obtaining Server Plugin Information). For example:

mysql> SELECT PLUGIN_NAME, PLUGIN_STATUS FROM INFORMATION_SCHEMA.PLUGINS
    -> WHERE PLUGIN_NAME LIKE 'audit%';
+-------------+---------------+
| PLUGIN_NAME | PLUGIN_STATUS |
+-------------+---------------+
| audit_log   | ACTIVE        |
+-------------+---------------+

If the plugin has been previously registered with INSTALL PLUGIN or is loaded with --plugin-load, you can use the --audit-log option at server startup to control plugin activation. For example, to load the plugin at startup and prevent it from being removed at runtime, use these options:

[mysqld]
plugin-load=audit_log.so
audit-log=FORCE_PLUS_PERMANENT

If it is desired to prevent the server from running without the audit plugin, use --audit-log with a value of FORCE or FORCE_PLUS_PERMANENT to force server startup to fail if the plugin does not initialize successfully.

For additional information about the parameters used to configure operation of the audit_log plugin, see Section 7.3.6.2, “Audit Log Options and System Variables”.

Audit log file contents are not encrypted. See Section 7.3.2, “MySQL Enterprise Audit Security Considerations”.

7.3.2 MySQL Enterprise Audit Security Considerations

Contents of the audit log file produced by the audit_log plugin are not encrypted and may contain sensitive information, such as the text of SQL statements. 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. The default file is audit.log in the data directory. This can be changed by setting the audit_log_file system variable at server startup.

7.3.3 The Audit Log File

Audit log file contents are not encrypted. See Section 7.3.2, “MySQL Enterprise Audit Security Considerations”.

The audit log file is written as XML, using UTF-8 (up to 4 bytes per character). The root element is <AUDIT>. The closing </AUDIT> tag of the root element is written when the audit log plugin terminates, so the tag is not present in the file while the plugin is active.

The root element contains <AUDIT_RECORD> elements, each of which provides information about an audited event.

In MySQL 5.6.14, a new audit log file format was implemented for better compatibility with Oracle Audit Vault. It is possible to select either the old or new format using the audit_log_format system variable, which has permitted values of OLD and NEW (default OLD).

If you change the value of audit_log_format, use this procedure to avoid writing log entries in one format to an existing log file that contains entries in a different format:

  1. Stop the server.

  2. Rename the current audit log file manually.

  3. Restart the server with the new value of audit_log_format. The audit log plugin will create a new log file, which will contain log entries in the selected format.

Here is a sample log file in the default (old) format, reformatted slightly for readability:

<?xml version="1.0" encoding="UTF-8"?>
<AUDIT>
  <AUDIT_RECORD
    TIMESTAMP="2012-08-02T14:52:12"
    NAME="Audit"
    SERVER_ID="1"
    VERSION="1"
    STARTUP_OPTIONS="--port=3306"
    OS_VERSION="i686-Linux"
    MYSQL_VERSION="5.6.10-log"/>
  <AUDIT_RECORD
    TIMESTAMP="2012-08-02T14:52:41"
    NAME="Connect"
    CONNECTION_ID="1"
    STATUS="0"
    USER="root"
    PRIV_USER="root"
    OS_LOGIN=""
    PROXY_USER=""
    HOST="localhost"
    IP="127.0.0.1"
    DB=""/>
  <AUDIT_RECORD
    TIMESTAMP="2012-08-02T14:53:45"
    NAME="Query"
    CONNECTION_ID="1"
    STATUS="0"
    SQLTEXT="INSERT INTO t1 () VALUES()"/>
  <AUDIT_RECORD
    TIMESTAMP="2012-08-02T14:53:51"
    NAME="Quit"
    CONNECTION_ID="1"
    STATUS="0"/>
  <AUDIT_RECORD
    TIMESTAMP="2012-08-06T14:21:03"
    NAME="NoAudit"
    SERVER_ID="1"/>
</AUDIT>

Here is a sample log file in the new format, reformatted slightly for readability:

<?xml version="1.0" encoding="UTF-8"?>
<AUDIT>
 <AUDIT_RECORD>
  <TIMESTAMP>2013-09-17T15:03:24 UTC</TIMESTAMP>
  <RECORD_ID>1_2013-09-17T15:03:24</RECORD_ID>
  <NAME>Audit</NAME>
  <SERVER_ID>1</SERVER_ID>
  <VERSION>1</VERSION>
  <STARTUP_OPTIONS>/usr/local/mysql/bin/mysqld
    --socket=/usr/local/mysql/mysql.sock
    --port=3306</STARTUP_OPTIONS>
  <OS_VERSION>x86_64-osx10.6</OS_VERSION>
  <MYSQL_VERSION>5.7.2-m12-log</MYSQL_VERSION>
 </AUDIT_RECORD>
 <AUDIT_RECORD>
  <TIMESTAMP>2013-09-17T15:03:40 UTC</TIMESTAMP>
  <RECORD_ID>2_2013-09-17T15:03:24</RECORD_ID>
  <NAME>Connect</NAME>
  <CONNECTION_ID>2</CONNECTION_ID>
  <STATUS>0</STATUS>
  <STATUS_CODE>0</STATUS_CODE>
  <USER>root</USER>
  <OS_LOGIN></OS_LOGIN>
  <HOST>localhost</HOST>
  <IP>127.0.0.1</IP>
  <COMMAND_CLASS>connect</COMMAND_CLASS>
  <PRIV_USER>root</PRIV_USER>
  <PROXY_USER></PROXY_USER>
  <DB>test</DB>
 </AUDIT_RECORD>
...
 <AUDIT_RECORD>
  <TIMESTAMP>2013-09-17T15:03:41 UTC</TIMESTAMP>
  <RECORD_ID>4_2013-09-17T15:03:24</RECORD_ID>
  <NAME>Query</NAME>
  <CONNECTION_ID>2</CONNECTION_ID>
  <STATUS>0</STATUS>
  <STATUS_CODE>0</STATUS_CODE>
  <USER>root[root] @ localhost [127.0.0.1]</USER>
  <OS_LOGIN></OS_LOGIN>
  <HOST>localhost</HOST>
  <IP>127.0.0.1</IP>
  <COMMAND_CLASS>drop_table</COMMAND_CLASS>
  <SQLTEXT>DROP TABLE IF EXISTS t</SQLTEXT>
 </AUDIT_RECORD>
 <AUDIT_RECORD>
  <TIMESTAMP>2013-09-17T15:03:41 UTC</TIMESTAMP>
  <RECORD_ID>5_2013-09-17T15:03:24</RECORD_ID>
  <NAME>Query</NAME>
  <CONNECTION_ID>2</CONNECTION_ID>
  <STATUS>0</STATUS>
  <STATUS_CODE>0</STATUS_CODE>
  <USER>root[root] @ localhost [127.0.0.1]</USER>
  <OS_LOGIN></OS_LOGIN>
  <HOST>localhost</HOST>
  <IP>127.0.0.1</IP>
  <COMMAND_CLASS>create_table</COMMAND_CLASS>
  <SQLTEXT>CREATE TABLE t (i INT)</SQLTEXT>
 </AUDIT_RECORD>
...
 <AUDIT_RECORD>
  <TIMESTAMP>2013-09-17T15:03:41 UTC</TIMESTAMP>
  <RECORD_ID>7_2013-09-17T15:03:24</RECORD_ID>
  <NAME>Quit</NAME>
  <CONNECTION_ID>2</CONNECTION_ID>
  <STATUS>0</STATUS>
  <STATUS_CODE>0</STATUS_CODE>
  <USER></USER>
  <OS_LOGIN></OS_LOGIN>
  <HOST></HOST>
  <IP></IP>
  <COMMAND_CLASS>connect</COMMAND_CLASS>
 </AUDIT_RECORD>
...
 <AUDIT_RECORD>
  <TIMESTAMP>2013-09-17T15:03:47 UTC</TIMESTAMP>
  <RECORD_ID>9_2013-09-17T15:03:24</RECORD_ID>
  <NAME>Shutdown</NAME>
  <CONNECTION_ID>3</CONNECTION_ID>
  <STATUS>0</STATUS>
  <STATUS_CODE>0</STATUS_CODE>
  <USER>root[root] @ localhost [127.0.0.1]</USER>
  <OS_LOGIN></OS_LOGIN>
  <HOST>localhost</HOST>
  <IP>127.0.0.1</IP>
  <COMMAND_CLASS></COMMAND_CLASS>
 </AUDIT_RECORD>
 <AUDIT_RECORD>
  <TIMESTAMP>2013-09-17T15:03:47 UTC</TIMESTAMP>
  <RECORD_ID>10_2013-09-17T15:03:24</RECORD_ID>
  <NAME>Quit</NAME>
  <CONNECTION_ID>3</CONNECTION_ID>
  <STATUS>0</STATUS>
  <STATUS_CODE>0</STATUS_CODE>
  <USER></USER>
  <OS_LOGIN></OS_LOGIN>
  <HOST></HOST>
  <IP></IP>
  <COMMAND_CLASS>connect</COMMAND_CLASS>
 </AUDIT_RECORD>
 <AUDIT_RECORD>
  <TIMESTAMP>2013-09-17T15:03:49 UTC</TIMESTAMP>
  <RECORD_ID>11_2013-09-17T15:03:24</RECORD_ID>
  <NAME>NoAudit</NAME>
  <SERVER_ID>1</SERVER_ID>
 </AUDIT_RECORD>
</AUDIT>

Attributes of <AUDIT_RECORD> elements have these characteristics:

  • Some attributes appear in every element, but most are optional and do not necessarily appear in every element.

  • Order of attributes within an element is not guaranteed.

  • Attribute values are not fixed length. Long values may be truncated as indicated in the attribute descriptions given later.

  • The <, >, ", and & characters are encoded as &lt;, &gt;, &quot;, and &amp;, respectively. NUL bytes (U+00) are encoded as the ? character.

  • Characters not valid as XML characters are encoded using numeric character references. Valid XML characters are:

    #x9 | #xA | #xD | [#x20-#xD7FF] | [#xE000-#xFFFD] | [#x10000-#x10FFFF]
    

New Audit Log File Format

Every <AUDIT_RECORD> element contains a set of mandatory elements. Other optional elements may appear, depending on the audit record type.

The following elements are mandatory in every <AUDIT_RECORD> element:

  • <NAME>

    A string representing the type of instruction that generated the audit event, such as a command that the server received from a client.

    Example:

    <NAME>Query</NAME>
    

    Some common <NAME> values:

    Audit    When auditing starts, which may be server startup time
    Connect  When a client connects, also known as logging in
    Query    An SQL statement (executed directly)
    Prepare  Preparation of an SQL statement; usually followed by Execute
    Execute  Execution of an SQL statement; usually follows Prepare
    Shutdown Server shutdown
    Quit     When a client disconnects
    NoAudit  Auditing has been turned off
    

    The possible values are Audit, Binlog Dump, Change user, Close stmt, Connect Out, Connect, Create DB, Daemon, Debug, Delayed insert, Drop DB, Execute, Fetch, Field List, Init DB, Kill, Long Data, NoAudit, Ping, Prepare, Processlist, Query, Quit, Refresh, Register Slave, Reset stmt, Set option, Shutdown, Sleep, Statistics, Table Dump, Time.

    With the exception of Audit and NoAudit, these values correspond to the COM_xxx command values listed in the mysql_com.h header file. For example, Create DB and Shutdown correspond to COM_CREATE_DB and COM_SHUTDOWN, respectively.

  • <RECORD_ID>

    A unique identifier for the audit record. The value is composed from a sequence number and timestamp, in the format SEQ_TIMESTAMP. The sequence number is initialized to the size of the audit log file at the time the audit log plugin opens it and increments by 1 for each record logged. The timestamp is a UTC value in yyyy-mm-ddThh:mm:ss format indicating the time when the audit log plugin opened the file.

    Example:

    <RECORD_ID>28743_2013-09-18T21:03:24</RECORD_ID>
    
  • <TIMESTAMP>

    The date and time that the audit event was generated. For example, the event corresponding to execution of an SQL statement received from a client has a <TIMESTAMP> value occurring after the statement finishes, not when it is received. The value has the format yyyy-mm-ddThh:mm:ss UTC (with T, no decimals). The format includes a time zone specifier at the end. The time zone is always UTC.

    Example:

    <TIMESTAMP>2013-09-17T15:03:49 UTC</TIMESTAMP>
    

The following elements are optional in <AUDIT_RECORD> elements. Many of them occur only with specific <NAME> values.

  • <COMMAND_CLASS>

    A string that indicates the type of action performed.

    Example:

    <COMMAND_CLASS>drop_table</COMMAND_CLASS>
    

    The values come from the com_status_vars array in the sql/mysqld.cc file in a MySQL source distribution. They correspond to the status variables displayed by this statement:

    SHOW STATUS LIKE 'Com%';
    
  • <CONNECTION_ID>

    An unsigned integer representing the client connection identifier. This is the same as the CONNECTION_ID() function value within the session.

    Example:

    <CONNECTION_ID>127</CONNECTION_ID>
    
  • <DB>

    A string representing the default database name. This element appears only if the <NAME> value is Connect or Change user.

  • <HOST>

    A string representing the client host name. This element appears only if the <NAME> value is Connect, Change user, or Query.

    Example:

    <HOST>localhost</HOST>
    
  • <IP>

    A string representing the client IP address. This element appears only if the <NAME> value is Connect, Change user, or Query.

    Example:

    <IP>127.0.0.1</IP>
    
  • <MYSQL_VERSION>

    A string representing the MySQL server version. This is the same as the value of the VERSION() function or version system variable. This element appears only if the <NAME> value is Audit.

    Example:

    <MYSQL_VERSION>5.7.1-m11-log</MYSQL_VERSION>
    
  • <OS_LOGIN>

    A string representing the external user name used during the authentication process, as set by the plugin used to authenticate the client. With native (built-in) MySQL authentication, or if the plugin does not set the value, this variable is NULL. The value is the same as that of the external_user system variable. See Section 5.8, “Proxy Users”.

    This element appears only if the <NAME> value is Connect, Change user, or Query.

  • <OS_VERSION>

    A string representing the operating system on which the server was built or is running. This element appears only if the <NAME> value is Audit.

    Example:

    <OS_VERSION>x86_64-Linux</OS_VERSION>
    
  • <PRIV_USER>

    A string representing the user that the server authenticated the client as. This is the user name that the server uses for privilege checking, and may differ from the <USER> value. This element appears only if the <NAME> value is Connect or Change user.

  • <PROXY_USER>

    A string representing the proxy user. The value is empty if user proxying is not in effect. This element appears only if the <NAME> value is Connect or Change user.

  • <SERVER_ID>

    An unsigned integer representing the server ID. This is the same as the value of the server_id system variable. This element appears only if the <NAME> value is Audit or NoAudit.

    Example:

    <SERVER_ID>1</SERVER_ID>
    
  • <SQLTEXT>

    A string representing the text of an SQL statement. The value can be empty. Long values may be truncated. This element appears only if the <NAME> value is Query or Execute.

    The string, like the audit log file itself, is written using UTF-8 (up to 4 bytes per character), so the value may be the result of conversion. For example, the original statement might have been received from the client as an SJIS string.

    Example:

    <SQLTEXT>DELETE FROM t1</SQLTEXT>
    
  • <STARTUP_OPTIONS>

    A string representing the options that were given on the command line or in option files when the MySQL server was started. This element appears only if the <NAME> value is Audit.

    Example:

    <STARTUP_OPTIONS>/usr/local/mysql/bin/mysqld
      --port=3306 --log-output=FILE</STARTUP_OPTIONS>
    
  • <STATUS>

    An unsigned integer representing the command status: 0 for success, nonzero if an error occurred. This is the same as the value of the mysql_errno() C API function.

    The audit log does not contain the SQLSTATE value or error message. To see the associations between error codes, SQLSTATE values, and messages, see Server Error Codes and Messages.

    Warnings are not logged.

    See the description for <STATUS_CODE> for information about how it differs from <STATUS>.

    Example:

    <STATUS>1051</STATUS>
    
  • <STATUS_CODE>

    An unsigned integer representing the command status: 0 for success, 1 if an error occurred.

    The STATUS_CODE value differs from the STATUS value: STATUS_CODE is 0 for success and 1 for error, which is compatible with the EZ_collector consumer for Audit Vault. STATUS is the value of the mysql_errno() C API function. This is 0 for success and nonzero for error, and thus is not necessarily 1 for error.

    Example:

    <STATUS_CODE>0</STATUS_CODE>
    
  • <USER>

    A string representing the user name sent by the client. This may differ from the <PRIV_USER> value. This element appears only if the <NAME> value is Connect, Change user, or Query.

    Example:

    <USER>root[root] @ localhost [127.0.0.1]</USER>
    
  • <VERSION>

    An unsigned integer representing the version of the audit log file format. This element appears only if the <NAME> value is Audit.

    Example:

    <VERSION>1</VERSION>
    

Old Audit Log File Format

Every <AUDIT_RECORD> element contains a set of mandatory attributes. Other optional attributes may appear depending on the audit record type.

The following attributes are mandatory in every <AUDIT_RECORD> element:

  • NAME

    A string representing the type of instruction that generated the audit event, such as a command that the server received from a client.

    Example: NAME="Query"

    Some common NAME values:

    "Audit"    When auditing starts, which may be server startup time
    "Connect"  When a client connects, also known as logging in
    "Query"    An SQL statement (executed directly)
    "Prepare"  Preparation of an SQL statement; usually followed by Execute
    "Execute"  Execution of an SQL statement; usually follows Prepare
    "Shutdown" Server shutdown
    "Quit"     When a client disconnects
    "NoAudit"  Auditing has been turned off
    

    The possible values are "Audit", "Binlog Dump", "Change user", "Close stmt", "Connect Out", "Connect", "Create DB", "Daemon", "Debug", "Delayed insert", "Drop DB", "Execute", "Fetch", "Field List", "Init DB", "Kill", "Long Data", "NoAudit", "Ping", "Prepare", "Processlist", "Query", "Quit", "Refresh", "Register Slave", "Reset stmt", "Set option", "Shutdown", "Sleep", "Statistics", "Table Dump", "Time".

    With the exception of "Audit" and "NoAudit", these values correspond to the COM_xxx command values listed in the mysql_com.h header file. For example, "Create DB" and "Shutdown" correspond to COM_CREATE_DB and COM_SHUTDOWN, respectively.

  • TIMESTAMP

    The date and time that the audit event was generated. For example, the event corresponding to execution of an SQL statement received from a client has a TIMESTAMP value occurring after the statement finishes, not when it is received. The value is UTC, in the format yyyy-mm-ddThh:mm:ss (with T, no decimals).

    Example: TIMESTAMP="2012-08-09T12:55:16"

The following attributes are optional in <AUDIT_RECORD> elements. Many of them occur only for elements with specific values of the NAME attribute.

  • CONNECTION_ID

    An unsigned integer representing the client connection identifier. This is the same as the CONNECTION_ID() function value within the session.

    Example: CONNECTION_ID="127"

  • DB

    A string representing the default database name. This attribute appears only if the NAME value is "Connect" or "Change user".

  • HOST

    A string representing the client host name. This attribute appears only if the NAME value is "Connect" or "Change user".

    Example: HOST="localhost"

  • IP

    A string representing the client IP address. This attribute appears only if the NAME value is "Connect" or "Change user".

    Example: IP="127.0.0.1"

  • MYSQL_VERSION

    A string representing the MySQL server version. This is the same as the value of the VERSION() function or version system variable. This attribute appears only if the NAME value is "Audit".

    Example: MYSQL_VERSION="5.6.11-log"

  • OS_LOGIN

    A string representing the external user (empty if none). The value may differ from USER, for example, if the server authenticates the client using an external authentication method. This attribute appears only if the NAME value is "Connect" or "Change user".

  • OS_VERSION

    A string representing the operating system on which the server was built or is running. This attribute appears only if the NAME value is "Audit".

    Example: OS_VERSION="x86_64-Linux"

  • PRIV_USER

    A string representing the user that the server authenticated the client as. This is the user name that the server uses for privilege checking, and may be different from the USER value. This attribute appears only if the NAME value is "Connect" or "Change user".

  • PROXY_USER

    A string representing the proxy user. The value is empty if user proxying is not in effect. This attribute appears only if the NAME value is "Connect" or "Change user".

  • SERVER_ID

    An unsigned integer representing the server ID. This is the same as the value of the server_id system variable. This attribute appears only if the NAME value is "Audit" or "NoAudit".

    Example: SERVER_ID="1"

  • SQLTEXT

    A string representing the text of an SQL statement. The value can be empty. Long values may be truncated. This attribute appears only if the NAME value is "Query" or "Execute".

    The string, like the audit log file itself, is written using UTF-8 (up to 4 bytes per character), so the value may be the result of conversion. For example, the original statement might have been received from the client as an SJIS string.

    Example: SQLTEXT="DELETE FROM t1"

  • STARTUP_OPTIONS

    A string representing the options that were given on the command line or in option files when the MySQL server was started. This attribute appears only if the NAME value is "Audit".

    Example: STARTUP_OPTIONS="--port=3306 --log-output=FILE"

  • STATUS

    An unsigned integer representing the command status: 0 for success, nonzero if an error occurred. This is the same as the value of the mysql_errno() C API function.

    The audit log does not contain the SQLSTATE value or error message. To see the associations between error codes, SQLSTATE values, and messages, see Server Error Codes and Messages.

    Warnings are not logged.

    Example: STATUS="1051"

  • USER

    A string representing the user name sent by the client. This may be different from the PRIV_USER value. This attribute appears only if the NAME value is "Connect" or "Change user".

  • VERSION

    An unsigned integer representing the version of the audit log file format. This attribute appears only if the NAME value is "Audit".

    Example: VERSION="1"

7.3.4 Audit Log Logging Control

This section describes how the audit_log plugin performs logging and the system variables that control how logging occurs. It assumes familiarity with the log file format described in Section 7.3.3, “The Audit Log File”.

The audit_log plugin can also control whether audited events are written to the audit log file based on the account from which events originate or event status. See Section 7.3.5, “Audit Log Filtering”.

When the audit log plugin opens its log file, it checks whether the XML declaration and opening <AUDIT> root element tag must be written and writes them if so. When the audit log plugin terminates, it writes a closing </AUDIT> tag to the file.

If the log file exists at open time, the plugin checks whether the file ends with an </AUDIT> tag and truncates it if so before writing any <AUDIT_RECORD> elements. If the log file exists but does not end with </AUDIT> or the </AUDIT> tag cannot be truncated, the plugin considers the file malformed and fails to initialize. This can occur if the server crashes or is killed with the audit log plugin running. No logging occurs until the problem is rectified. Check the error log for diagnostic information:

[ERROR] Plugin 'audit_log' init function returned error.

To deal with this problem, either remove or rename the malformed log file and restart the server.

The MySQL server calls the audit log plugin to write an <AUDIT_RECORD> element whenever an auditable event occurs, such as when it completes execution of an SQL statement received from a client. Typically the first <AUDIT_RECORD> element written after server startup has the server description and startup options. Elements following that one represent events such as client connect and disconnect events, executed SQL statements, and so forth. Only top-level statements are logged, not statements within stored programs such as triggers or stored procedures. Contents of files referenced by statements such as LOAD DATA INFILE are not logged.

To permit control over how logging occurs, the audit_log plugin provides several system variables, described following. For more information, see Section 7.3.6.2, “Audit Log Options and System Variables”.

Audit Log File Naming

To control the audit log file name, set the audit_log_file system variable at server startup. By default, the name is audit.log in the server data directory. For security reasons, the audit log file should be written to a directory accessible only to the MySQL server and users with a legitimate reason to view the log.

Audit Logging Strategy

The audit log plugin can use any of several strategies for log writes. To specify a strategy, set the audit_log_strategy system variable at server startup. By default, the strategy value is ASYNCHRONOUS and the plugin logs asynchronously to a buffer, waiting if the buffer is full. It's possible to tell the plugin not to wait (PERFORMANCE) or to log synchronously, either using file system caching (SEMISYNCHRONOUS) or forcing output with a sync() call after each write request (SYNCHRONOUS).

Asynchronous logging strategy has these characteristics:

  • Minimal impact on server performance and scalability.

  • Blocking of threads that generate audit events for the shortest possible time; that is, time to allocate the buffer plus time to copy the event to the buffer.

  • Output goes to the buffer. A separate thread handles writes from the buffer to the log file.

A disadvantage of PERFORMANCE strategy is that it drops events when the buffer is full. For a heavily loaded server, it is more likely that the audit log will be missing events.

With asynchronous logging, the integrity of the log file may be compromised if a problem occurs during a write to the file or if the plugin does not shut down cleanly (for example, in the event that the server host crashes). To reduce this risk, set audit_log_strategy to use synchronous logging. Regardless of strategy, logging occurs on a best-effort basis, with no guarantee of consistency.

If the file system to which the audit log is being written fills up, a disk full error is written to the error log. Audit logging continues until the audit log buffer is full. If free disk space has not been made available by the time the buffer fills, client sessions will hang, and stopping the server at the time of client sessions hanging will result in audit log corruption. To avoid this if client sessions are hung, ensure that free space is available on the audit logging file system before stopping the server.

Audit Log Space Management

The audit log plugin provides several system variables that enable you to manage the space used by its log files:

  • audit_log_buffer_size: Set this variable at server startup to set the size of the buffer for asynchronous logging. The plugin uses a single buffer, which it allocates when it initializes and removes when it terminates. The plugin allocates this buffer only if logging is asynchronous.

  • audit_log_rotate_on_size, audit_log_flush: These variables permit audit log file rotation and flushing. The audit log file has the potential to grow very large and consume a lot of disk space. To manage the space used, either enable automatic log rotation, or manually rename the audit file and flush the log to open a new file. The renamed file can be removed or backed up as desired.

    By default, audit_log_rotate_on_size=0 and there is no log rotation. In this case, the audit log plugin closes and reopens the log file when the audit_log_flush value changes from disabled to enabled. Log file renaming must be done externally to the server. Suppose that you want to maintain the three most recent log files, which cycle through the names audit.log.1 through audit.log.3. On Unix, perform rotation manually like this:

    1. From the command line, rename the current log files:

      mv audit.log.2 audit.log.3
      mv audit.log.1 audit.log.2
      mv audit.log audit.log.1
      

      At this point, the plugin is still writing to the current log file, which has been renamed to audit.log.1.

    2. Connect to the server and flush the log file so the plugin closes it and reopens a new audit.log file:

      SET GLOBAL audit_log_flush = ON;
      

    If audit_log_rotate_on_size is greater than 0, setting audit_log_flush has no effect. In this case, the audit log plugin closes and reopens its log file whenever a write to the file causes its size to exceed the audit_log_rotate_on_size value. The plugin renames the original file to have a timestamp extension. For example, audit.log might be renamed to audit.log.13440033615657730. The last 7 digits are a fractional second part. The first 10 digits are a Unix timestamp value that can be interpreted using the FROM_UNIXTIME() function:

    mysql> SELECT FROM_UNIXTIME(1344003361);
    +---------------------------+
    | FROM_UNIXTIME(1344003361) |
    +---------------------------+
    | 2012-08-03 09:16:01       |
    +---------------------------+
    

7.3.5 Audit Log Filtering

The audit_log plugin can filter audited events. This enables you to control whether audited events are written to the audit log file based on the account from which events originate or event status. Status filtering occurs separately for connection events and statement events.

Event Filtering by Account

As of MySQL 5.6.20, to filter audited events based on the originating account, set one of these system variables at server startup or runtime:

  • audit_log_include_accounts: The accounts to include in audit logging. If this variable is set, only these accounts are audited.

  • audit_log_exclude_accounts: The accounts to exclude from audit logging. If this variable is set, all but these accounts are audited.

The value for either variable can be NULL or a string containing one or more comma-separated account names, each in user_name@host_name format. By default, both variables are NULL, in which case, no account filtering is done and auditing occurs for all accounts.

Modifications to audit_log_include_accounts or audit_log_exclude_accounts affect only connections created subsequent to the modification, not existing connections.

Example: To enable audit logging only for the user1 and user2 local host account accounts, set the audit_log_include_accounts system variable like this:

SET GLOBAL audit_log_include_accounts = 'user1@localhost,user2@localhost';

Only one of audit_log_include_accounts or audit_log_exclude_accounts can be non-NULL at a time:

-- This sets audit_log_exclude_accounts to NULL
SET GLOBAL audit_log_include_accounts = value;
-- This fails because audit_log_include_accounts is not NULL
SET GLOBAL audit_log_exclude_accounts = value;
-- To set audit_log_exclude_accounts, first set
-- audit_log_include_accounts to NULL
SET GLOBAL audit_log_include_accounts = NULL;
SET GLOBAL audit_log_exclude_accounts = value;

If you inspect the value of either variable, be aware that SHOW VARIABLES displays NULL as an empty string. To avoid this, use SELECT instead:

mysql> SHOW VARIABLES LIKE 'audit_log_include_accounts';
+----------------------------+-------+
| Variable_name              | Value |
+----------------------------+-------+
| audit_log_include_accounts |       |
+----------------------------+-------+
mysql> SELECT @@audit_log_include_accounts;
+------------------------------+
| @@audit_log_include_accounts |
+------------------------------+
| NULL                         |
+------------------------------+

If a user name or host name requires quoting because it contains a comma, space, or other special character, quote it using single quotes. If the variable value itself is quoted with single quotes, double each inner single quote or escape it with a backslash. The following statements each enable audit logging for the local root account and are equivalent, even though the quoting styles differ:

SET GLOBAL audit_log_include_accounts = 'root@localhost';
SET GLOBAL audit_log_include_accounts = '''root''@''localhost''';
SET GLOBAL audit_log_include_accounts = '\'root\'@\'localhost\'';
SET GLOBAL audit_log_include_accounts = "'root'@'localhost'";

The last statement will not work if the ANSI_QUOTES SQL mode is enabled because in that mode double quotes signify identifier quoting, not string quoting.

Event Filtering by Status

As of MySQL 5.6.20, to filter audited events based on status, set these system variables at server startup or runtime:

Each variable takes a value of ALL (log all associated events; this is the default), ERRORS (log only failed events), or NONE (do not log events). For example, to log all statement events but only failed connection events, use these settings:

SET GLOBAL audit_log_statement_policy = ALL;
SET GLOBAL audit_log_connection_policy = ERRORS;

Before MySQL 5.6.20, audit_log_connection_policy and audit_log_statement_policy are not available. Instead, use audit_log_policy at server startup or runtime. It takes a value of ALL (log all events; this is the default), LOGINS (log connection events), QUERIES (log statement events), or NONE (do not log events). For any of those values, the audit log plugin logs all selected events without distinction as to success or failure.

As of MySQL 5.6.20, audit_log_policy is still available but can be set only at server startup. At runtime, it is a read-only variable. Its use at startup works as follows:

  • If you do not set audit_log_policy or set it to its default of ALL, any explicit settings for audit_log_connection_policy or audit_log_statement_policy apply as specified. If not specified, they default to ALL.

  • If you set audit_log_policy to a non-ALL value, that value takes precedence over and is used to set audit_log_connection_policy and audit_log_statement_policy, as indicated in the following table. If you also set either of those variables to a value other than their default of ALL, the server writes a message to the error log to indicate that their values are being overridden.

    Startup audit_log_policy ValueResulting audit_log_connection_policy ValueResulting audit_log_statement_policy Value
    LOGINSALLNONE
    QUERIESNONEALL
    NONENONENONE

Event Filtering Reporting

To check the effect of filtering, you can inspect the values of these status variables:

  • Audit_log_events: The number of events handled by the audit log plugin, whether or not they were written to the log based on filtering policy.

  • Audit_log_events_filtered: The number of events handled by the audit log plugin that were filtered (not written to the log) based on filtering policy.

  • Audit_log_events_written: The number of events written to the audit log.

These variables are available as of MySQL 5.6.20.

7.3.6 Audit Log Reference

The following discussion serves as a reference to these MySQL Enterprise Audit components:

  • Audit log system variables

  • Audit log status variables

7.3.6.1 Audit Log Option and Variable Reference

Table 7.9 Audit Log Option/Variable Reference

NameCmd-LineOption FileSystem VarStatus VarVar ScopeDynamic
audit-logYesYes    
audit_log_buffer_sizeYesYesYes GlobalNo
audit_log_connection_policyYesYesYes GlobalYes
audit_log_current_session  Yes BothNo
Audit_log_current_size   YesGlobalNo
Audit_log_event_max_drop_size   YesGlobalNo
Audit_log_events   YesGlobalNo
Audit_log_events_filtered   YesGlobalNo
Audit_log_events_lost   YesGlobalNo
Audit_log_events_written   YesGlobalNo
audit_log_exclude_accountsYesYesYes GlobalYes
audit_log_fileYesYesYes GlobalNo
audit_log_flush  Yes GlobalYes
audit_log_formatYesYesYes GlobalNo
audit_log_include_accountsYesYesYes GlobalYes
audit_log_policyYesYesYes GlobalVaries
audit_log_rotate_on_sizeYesYesYes GlobalYes
audit_log_statement_policyYesYesYes GlobalYes
audit_log_strategyYesYesYes GlobalNo
Audit_log_total_size   YesGlobalNo
Audit_log_write_waits   YesGlobalNo

7.3.6.2 Audit Log Options and System Variables

This section describes the command options and system variables that control operation of MySQL Enterprise Audit. If values specified at startup time are incorrect, the audit_log plugin may fail to initialize properly and the server does not load it. In this case, the server may also produce error messages for other audit log settings because it will not recognize them.

To control the activation of the audit_log plugin, use this option:

If the audit_log plugin is enabled, it exposes several system variables that permit control over logging:

mysql> SHOW VARIABLES LIKE 'audit_log%';
+-----------------------------+--------------+
| Variable_name               | Value        |
+-----------------------------+--------------+
| audit_log_buffer_size       | 1048576      |
| audit_log_connection_policy | ALL          |
| audit_log_current_session   | ON           |
| audit_log_exclude_accounts  |              |
| audit_log_file              | audit.log    |
| audit_log_flush             | OFF          |
| audit_log_format            | OLD          |
| audit_log_include_accounts  |              |
| audit_log_policy            | ALL          |
| audit_log_rotate_on_size    | 0            |
| audit_log_statement_policy  | ALL          |
| audit_log_strategy          | ASYNCHRONOUS |
+-----------------------------+--------------+

You can set any of these variables at server startup, and some of them at runtime.

  • audit_log_buffer_size

    Introduced5.6.10
    Command-Line Format--audit_log_buffer_size=value
    System VariableNameaudit_log_buffer_size
    Variable ScopeGlobal
    Dynamic VariableNo
    Permitted Values (32-bit platforms)Typeinteger
    Default1048576
    Min Value4096
    Max Value4294967295
    Permitted Values (64-bit platforms)Typeinteger
    Default1048576
    Min Value4096
    Max Value18446744073709547520

    When the audit log plugin writes events to the log asynchronously, it uses a buffer to store event contents prior to writing them. This variable controls the size of that buffer, in bytes. The server adjusts the value to a multiple of 4096. The plugin uses a single buffer, which it allocates when it initializes and removes when it terminates. The plugin allocates this buffer only if logging is asynchronous.

  • audit_log_connection_policy

    Introduced5.6.20
    Command-Line Format--audit_log_connection_policy=value
    System VariableNameaudit_log_connection_policy
    Variable ScopeGlobal
    Dynamic VariableYes
    Permitted ValuesTypeenumeration
    DefaultALL
    Valid ValuesALL
    ERRORS
    NONE

    The policy controlling how the audit log plugin writes connection events to its log file. The following table shows the permitted values.

    ValueDescription
    ALLLog all connection events
    ERRORSLog only failed connection events
    NONEDo not log connection events
    Note

    At server startup, any explicit value given for audit_log_connection_policy may be overridden if audit_log_policy is also specified, as described in Section 7.3.4, “Audit Log Logging Control”.

  • audit_log_current_session

    Introduced5.6.20
    System VariableNameaudit_log_current_session
    Variable ScopeGlobal, Session
    Dynamic VariableNo
    Permitted ValuesTypeboolean
    Defaultdepends on filtering policy

    Whether audit logging is enabled for the current session. The session value of this variable is read only. It is set when the session begins based on the values of the audit_log_include_accounts and audit_log_exclude_accounts system variables. The audit log plugin uses the session value to determine whether to audit events for the session. (There is a global value, but the plugin does not use it.)

  • audit_log_exclude_accounts

    Introduced5.6.20
    Command-Line Format--audit_log_exclude_accounts=value
    System VariableNameaudit_log_exclude_accounts
    Variable ScopeGlobal
    Dynamic VariableYes
    Permitted ValuesTypestring
    DefaultNULL

    The accounts for which events should not be logged. The value should be NULL or a string containing a list of one or more comma-separated account names. For more information, see Section 7.3.4, “Audit Log Logging Control”.

    Modifications to audit_log_exclude_accounts affect only connections created subsequent to the modification, not existing connections.

  • audit_log_file

    Introduced5.6.10
    Command-Line Format--audit_log_file=file_name
    System VariableNameaudit_log_file
    Variable ScopeGlobal
    Dynamic VariableNo
    Permitted ValuesTypefile name
    Defaultaudit.log

    The name of the file to which the audit log plugin writes events. The default value is audit.log. If the value of audit_log_file is a relative path name, the server interprets it relative to the data directory. If the value is a full path name, the server uses the value as is. A full path name may be useful if it is desirable to locate audit files on a separate file system or directory. For security reasons, the audit log file should be written to a directory accessible only to the MySQL server and users with a legitimate reason to view the log. For more information, see Section 7.3.4, “Audit Log Logging Control”.

  • audit_log_flush

    Introduced5.6.10
    System VariableNameaudit_log_flush
    Variable ScopeGlobal
    Dynamic VariableYes
    Permitted ValuesTypeboolean
    DefaultOFF

    When this variable is set to enabled (1 or ON), the audit log plugin closes and reopens its log file to flush it. (The value remains OFF so that you need not disable it explicitly before enabling it again to perform another flush.) Enabling this variable has no effect unless audit_log_rotate_on_size is 0. For more information, see Section 7.3.4, “Audit Log Logging Control”.

  • audit_log_format

    Introduced5.6.14
    Command-Line Format--audit_log_format=value
    System VariableNameaudit_log_format
    Variable ScopeGlobal
    Dynamic VariableNo
    Permitted Values (>= 5.6.14)Typeenumeration
    DefaultOLD
    Valid ValuesOLD
    NEW

    The audit log file format. Permitted values are OLD and NEW (default OLD). For details about each format, see Section 7.3.3, “The Audit Log File”.

    If you change the value of audit_log_format, use this procedure to avoid writing log entries in one format to an existing log file that contains entries in a different format:

    1. Stop the server.

    2. Rename the current audit log file manually.

    3. Restart the server with the new value of audit_log_format. The audit log plugin will create a new log file, which will contain log entries in the selected format.

  • audit_log_include_accounts

    Introduced5.6.20
    Command-Line Format--audit_log_include_accounts=value
    System VariableNameaudit_log_include_accounts
    Variable ScopeGlobal
    Dynamic VariableYes
    Permitted ValuesTypestring
    DefaultNULL

    The accounts for which events should be logged. The value should be NULL or a string containing a list of one or more comma-separated account names. For more information, see Section 7.3.4, “Audit Log Logging Control”.

    Modifications to audit_log_include_accounts affect only connections created subsequent to the modification, not existing connections.

  • audit_log_policy

    Introduced5.6.10
    Command-Line Format--audit_log_policy=value
    System Variable (<= 5.6.19)Nameaudit_log_policy
    Variable ScopeGlobal
    Dynamic VariableYes
    System Variable (>= 5.6.20)Nameaudit_log_policy
    Variable ScopeGlobal
    Dynamic VariableNo
    Permitted ValuesTypeenumeration
    DefaultALL
    Valid ValuesALL
    LOGINS
    QUERIES
    NONE

    The policy controlling how the audit log plugin writes events to its log file. The following table shows the permitted values.

    ValueDescription
    ALLLog all events
    LOGINSLog only login events
    QUERIESLog only query events
    NONELog nothing (disable the audit stream)

    As of MySQL 5.6.20, audit_log_policy can be set only at server startup. At runtime, it is a read-only variable. This is due to the introduction of two other system variables, audit_log_connection_policy and audit_log_statement_policy, that provide finer control over logging policy and that can be set either at startup or at runtime. If you continue to use audit_log_policy at startup instead of the other two variables, the server uses its value to set those variables. For more information about the policy variables and their interaction, see Section 7.3.4, “Audit Log Logging Control”.

    Before MySQL 5.6.20, the audit_log_connection_policy and audit_log_statement_policy system variables do not exist. audit_log_policy is the only policy control variable and it can be set at server startup or runtime.

  • audit_log_rotate_on_size

    Introduced5.6.10
    Command-Line Format--audit_log_rotate_on_size=N
    System VariableNameaudit_log_rotate_on_size
    Variable ScopeGlobal
    Dynamic VariableYes
    Permitted ValuesTypeinteger
    Default0

    If the audit_log_rotate_on_size value is greater than 0, the audit log plugin closes and reopens its log file if a write to the file causes its size to exceed this value. The original file is renamed to have a timestamp extension.

    If the audit_log_rotate_on_size value is 0, the plugin does not close and reopen its log based on size. Instead, use audit_log_flush to close and reopen the log on demand. In this case, rename the file externally to the server before flushing it.

    For more information about audit log file rotation and timestamp interpretation, see Section 7.3.4, “Audit Log Logging Control”.

    If you set this variable to a value that is not a multiple of 4096, it is truncated to the nearest multiple. (Thus, setting it to a value less than 4096 has the effect of setting it to 0 and no rotation occurs.)

  • audit_log_statement_policy

    Introduced5.6.20
    Command-Line Format--audit_log_statement_policy=value
    System VariableNameaudit_log_statement_policy
    Variable ScopeGlobal
    Dynamic VariableYes
    Permitted ValuesTypeenumeration
    DefaultALL
    Valid ValuesALL
    ERRORS
    NONE

    The policy controlling how the audit log plugin writes statement events to its log file. The following table shows the permitted values.

    ValueDescription
    ALLLog all statement events
    ERRORSLog only failed statement events
    NONEDo not log statement events
    Note

    At server startup, any explicit value given for audit_log_statement_policy may be overridden if audit_log_policy is also specified, as described in Section 7.3.4, “Audit Log Logging Control”.

  • audit_log_strategy

    Introduced5.6.10
    Command-Line Format--audit_log_strategy=value
    System VariableNameaudit_log_strategy
    Variable ScopeGlobal
    Dynamic VariableNo
    Permitted ValuesTypeenumeration
    DefaultASYNCHRONOUS
    Valid ValuesASYNCHRONOUS
    PERFORMANCE
    SEMISYNCHRONOUS
    SYNCHRONOUS

    The logging method used by the audit log plugin. The following table describes the permitted values.

    Table 7.10 Audit Log Strategies

    ValueMeaning
    ASYNCHRONOUSLog asynchronously, wait for space in output buffer
    PERFORMANCELog asynchronously, drop request if insufficient space in output buffer
    SEMISYNCHRONOUSLog synchronously, permit caching by operating system
    SYNCHRONOUSLog synchronously, call sync() after each request

7.3.6.3 Audit Log Plugin Status Variables

If the audit_log plugin is enabled, it exposes several status variables that provide operational information.

7.3.7 Audit Log Restrictions

MySQL Enterprise Audit is subject to these general restrictions:

  • Only SQL statements are logged. Changes made by no-SQL APIs, such as memcached, Node.JS, and the NDB API, are not logged.

  • Only top-level statements are logged, not statements within stored programs such as triggers or stored procedures.

  • Contents of files referenced by statements such as LOAD DATA INFILE are not logged.

MySQL Cluster.  It is possible to use MySQL Enterprise Audit with MySQL Cluster, subject to the following conditions:

  • All changes to be logged must be done using the SQL interface. Changes using no-SQL interfaces, such as those provided by the NDB API, memcached, or ClusterJ, are not logged.

  • The plugin must be installed on each MySQL server that is used to execute SQL on the cluster.

  • Audit plugin data must be aggregated amongst all MySQL servers used with the cluster. This aggregation is the responsibility of the application or user.

7.4 MySQL Enterprise Firewall

Note

MySQL Enterprise Firewall is an extension included in MySQL Enterprise Edition, a commercial product. To learn more about commercial products, see http://www.mysql.com/products/.

As of MySQL 5.6.24, MySQL Enterprise Edition includes MySQL Enterprise Firewall, an application-level firewall that enables database administrators to permit or deny SQL statement execution based on matching against whitelists of accepted statement patterns. This helps harden MySQL Server against attacks such as SQL injection or attempts to exploit applications by using them outside of their legitimate query workload characteristics.

Each MySQL account registered with the firewall has its own statement whitelist, enabling protection to be tailored per account. For a given account, the firewall can operate in recording, protecting, or detecting mode, for training in the accepted statement patterns, active protection against unacceptable statements, or passive detection of unacceptable statements. The diagram illustrates how the firewall processes incoming statements in each mode.

Figure 7.1 MySQL Enterprise Firewall Operation

MySQL Enterprise Firewall Operation

The following sections describe the components of MySQL Enterprise Firewall, discuss how to install and use it, and provide reference information for its components.

7.4.1 MySQL Enterprise Firewall Components

MySQL Enterprise Firewall is based on a plugin library that implements these components:

  • A server-side plugin named MYSQL_FIREWALL examines SQL statements before they execute and, based on its in-memory cache, renders a decision whether to execute or reject each statement.

  • Server-side plugins named MYSQL_FIREWALL_USERS and MYSQL_FIREWALL_WHITELIST implement INFORMATION_SCHEMA tables that provide views into the firewall data cache.

  • System tables named firewall_users and firewall_whitelist in the mysql database provide persistent storage of firewall data.

  • Stored procedures named sp_set_firewall_mode() and sp_reload_firewall_rules() perform tasks such as registering MySQL accounts with the firewall, establishing their operational mode, and managing transfer of firewall data between the cache and the underlying system tables.

  • A set of user-defined functions provides an SQL-level API for lower-level tasks such as synchronizing the cache with the underlying system tables.

  • System variables enable firewall configuration and status variables provide runtime operational information.

7.4.2 Installing or Uninstalling MySQL Enterprise Firewall

MySQL Enterprise Firewall installation is a one-time operation that installs the components described in Section 7.4.1, “MySQL Enterprise Firewall Components”. Installation can be performed using a graphical interface or manually:

  • On Windows, MySQL Installer includes an option to enable MySQL Enterprise Firewall for you.

  • MySQL Workbench 6.3.4 or higher can install MySQL Enterprise Firewall, enable or disable an installed firewall, or uninstall the firewall.

  • Manual MySQL Enterprise Firewall installation involves running a script located in the share directory of your MySQL installation.

Note

If installed, MySQL Enterprise Firewall involves some minimal overhead even when disabled. To avoid this overhead, do not install the firewall unless you plan to use it.

Note

MySQL Enterprise Firewall does not work together with the query cache. If the query cache is enabled, disable it before installing the firewall (see Query Cache Configuration).

For usage instructions, see Section 7.4.3, “Using MySQL Enterprise Firewall”. For reference information, see Section 7.4.4, “MySQL Enterprise Firewall Reference”.

Installing MySQL Enterprise Firewall

If MySQL Enterprise Firewall is already installed from an older version of MySQL, uninstall it using the instructions given later in this section and restart your server before installing the current version. In this case, it is also necessary to register your configuration again.

On Windows, you can use MySQL Installer to install MySQL Enterprise Firewall, as shown in Figure 7.2, “MySQL Enterprise Firewall Installation on Windows”. Check the Enable Enterprise Firewall checkbox. (Open Firewall port for network access has a different purpose. It refers to Windows Firewall and controls whether Windows blocks the TCP/IP port on which the MySQL server listens for client connections.)

Figure 7.2 MySQL Enterprise Firewall Installation on Windows

MySQL Enterprise Firewall Installation on Windows

To install MySQL Enterprise Firewall using MySQL Workbench 6.3.4 or higher, see MySQL Enterprise Firewall Interface.

To install MySQL Enterprise Firewall manually, look in the share directory of your MySQL installation and choose the script that is appropriate for your platform. The available scripts differ in the suffix used to refer to the plugin library file:

  • win_install_firewall.sql: Choose this script for Windows systems that use .dll as the file name suffix.

  • linux_install_firewall.sql: Choose this script for Linux and similar systems that use .so as the file name suffix.

The installation script creates stored procedures in the default database, so choose a database to use. Then run the script as follows, naming the chosen database on the command line. The example here uses the mysql database and the Linux installation script. Make the appropriate substitutions for your system.

shell> mysql -u root -p mysql < linux_install_firewall.sql
Enter password: (enter root password here)

Installing MySQL Enterprise Firewall either using a graphical interface or manually should enable the firewall. To verify that, connect to the server and execute this statement:

mysql> SHOW GLOBAL VARIABLES LIKE 'mysql_firewall_mode';
+---------------------+-------+
| Variable_name       | Value |
+---------------------+-------+
| mysql_firewall_mode | ON    |
+---------------------+-------+

Uninstalling MySQL Enterprise Firewall

MySQL Enterprise Firewall can be uninstalled using MySQL Workbench or manually.

To uninstall MySQL Enterprise Firewall using MySQL Workbench 6.3.4 or higher, see MySQL Enterprise Firewall Interface.

To uninstall MySQL Enterprise Firewall manually, execute the following statements. It is assumed that the stored procedures were created in the mysql database. Adjust the DROP PROCEDURE statements appropriately if the procedures were created in a different database.

DROP TABLE mysql.firewall_whitelist;
DROP TABLE mysql.firewall_users;
UNINSTALL PLUGIN mysql_firewall;
UNINSTALL PLUGIN mysql_firewall_whitelist;
UNINSTALL PLUGIN mysql_firewall_users;
DROP FUNCTION set_firewall_mode;
DROP FUNCTION normalize_statement;
DROP FUNCTION read_firewall_whitelist;
DROP FUNCTION read_firewall_users;
DROP FUNCTION mysql_firewall_flush_status;     # MySQL 5.6.26 and up only
DROP PROCEDURE mysql.sp_set_firewall_mode;
DROP PROCEDURE mysql.sp_reload_firewall_rules; # MySQL 5.6.26 and up only

7.4.3 Using MySQL Enterprise Firewall

Before using MySQL Enterprise Firewall, install it according to the instructions provided at Section 7.4.2, “Installing or Uninstalling MySQL Enterprise Firewall”. Also, MySQL Enterprise Firewall does not work together with the query cache; disable the query cache if it is enabled (see Query Cache Configuration).

This section describes how to configure MySQL Enterprise Firewall using SQL statements. Alternatively, MySQL Workbench 6.3.4 or higher provides a graphical interface for firewall control. See MySQL Enterprise Firewall Interface.

To enable or disable the firewall, set the mysql_firewall_mode system variable. By default, this variable is enabled when the firewall is installed. To control the initial firewall state explicitly, you can set the variable at server startup. For example, to enable the firewall in an option file, use these lines:

[mysqld]
mysql_firewall_mode=ON

It is also possible to disable or enable the firewall at runtime:

mysql> SET GLOBAL mysql_firewall_mode = OFF;
mysql> SET GLOBAL mysql_firewall_mode = ON;

In addition to the global on/off firewall mode, each account registered with the firewall has its own operational mode. For an account in recording mode, the firewall learns an application's fingerprint, that is, the acceptable statement patterns that, taken together, form a whitelist. After training, switch the firewall to protecting mode to harden MySQL against access by statements that deviate from the fingerprint. For additional training, switch the firewall back to recording mode as necessary to update the whitelist with new statement patterns. As of MySQL 5.6.26, an intrusion-detection mode is available that writes suspicious statements to the error log but does not deny access.

The firewall maintains whitelist rules on a per-account basis, enabling implementation of protection strategies such as these:

  • For an application that has unique protection requirements, configure it to use an account that is not used for any other purpose.

  • For applications that are related and share protection requirements, configure them as a group to use the same account.

Firewall operation is based on conversion of SQL statements to normalized digest form. Firewall digests are like the statement digests used by the Performance Schema (see Performance Schema Statement Digests). However, unlike the Performance Schema, the relevant digest-related system variable is max_digest_length.

For a connection from a registered account, the firewall converts each incoming statement to normalized form and processes it according to the account mode:

  • In recording mode, the firewall adds the normalized statement to the account whitelist rules.

  • In protecting mode, the firewall compares the normalized statement to the account whitelist rules. If there is a match, the statement passes and the server continues to process it. Otherwise, the server rejects the statement and returns an error to the client. As of MySQL 5.6.25, the firewall also writes the rejected statement to the error log if the mysql_firewall_trace system variable is enabled.

  • In detecting mode, the firewall matches statements as in protecting mode, but writes nonmatching statements to the error log without denying access.

Accounts that have a mode of OFF or are not registered with the firewall are ignored by it.

Note

Before MySQL 5.6.25, MySQL Enterprise Firewall records prepared statements as they are received by the server, not as normalized digests. Thus, spaces, tabs, and lettercase are signficant for comparison of whitelist rules against incoming statements.

To protect an account using MySQL Enterprise Firewall, follow these steps:

  1. Register the account and put it in recording mode.

  2. Connect to the MySQL server using the registered account and execute statements to be learned. This establishes the account's whitelist of accepted statements.

  3. Switch the registered account to protecting mode.

The following example shows how to register an account with the firewall, use the firewall to learn acceptable statements for that account, and protect the account against execution of unacceptable statements. The example account, 'fwuser'@'localhost', is for use by an application that accesses tables in the sakila database. (This database is available at http://dev.mysql.com/doc/index-other.html.)

Note

The user and host parts of the account name are quoted separately for statements such as CREATE USER and GRANT, whereas to specify an account for use with a firewall component, name it as a single quoted string 'fwuser@localhost'.

The convention for naming accounts as a single quoted string for firewall components means that you cannot use accounts that have embedded @ characters in the user name.

Perform the steps in the following procedure using an administrative MySQL account, except those designated for execution by the account registered with the firewall. The default database should be sakila for statements executed using the registered account.

  1. If necessary, create the account to be protected (choose an appropriate password) and grant it privileges for the sakila database:

    mysql> CREATE USER 'fwuser'@'localhost' IDENTIFIED BY 'fWp@3sw0rd';
    mysql> GRANT ALL ON sakila.* TO 'fwuser'@'localhost';
    
  2. Use the sp_set_firewall_mode() stored procedure to register the account with the firewall and place it in recording mode (if the procedure is located in a database other than mysql, adjust the statement accordingly):

    mysql> CALL mysql.sp_set_firewall_mode('fwuser@localhost', 'RECORDING');
    

    During the course of its execution, the stored procedure invokes firewall user-defined functions, which may produce output of their own.

  3. Using the registered account, connect to the server, then execute some statements that are legitimate for it:

    mysql> SELECT first_name, last_name FROM customer WHERE customer_id = 1;
    mysql> UPDATE rental SET return_date = NOW() WHERE rental_id = 1;
    mysql> SELECT get_customer_balance(1, NOW());
    

    The firewall converts the statements to digest form and records them in the account whitelist.

    Note

    Until the account executes statements in recording mode, its whitelist is empty, which is equivalent to deny all. If switched to protecting mode, the account will be effectively prohibited from executing statements.

  4. At this point, the user and whitelist information is cached and can be seen in the firewall INFORMATION_SCHEMA tables:

    mysql> SELECT MODE FROM INFORMATION_SCHEMA.MYSQL_FIREWALL_USERS
        -> WHERE USERHOST = 'fwuser@localhost';
    +-----------+
    | MODE      |
    +-----------+
    | RECORDING |
    +-----------+
    mysql> SELECT RULE FROM INFORMATION_SCHEMA.MYSQL_FIREWALL_WHITELIST
        -> WHERE USERHOST = 'fwuser@localhost';
    +----------------------------------------------------------------------------+
    | RULE                                                                       |
    +----------------------------------------------------------------------------+
    | SELECT `first_name` , `last_name` FROM `customer` WHERE `customer_id` = ?  |
    | SELECT `get_customer_balance` ( ? , NOW ( ) )                              |
    | UPDATE `rental` SET `return_date` = NOW ( ) WHERE `rental_id` = ?          |
    | SELECT @@`version_comment` LIMIT ?                                         |
    +----------------------------------------------------------------------------+
    
    Note

    The @@version_comment rule comes from a statement sent automatically by the mysql client when you connect to the server as the registered user.

    It is important to train the firewall under conditions matching application use. For example, a given MySQL connector might send statements to the server at the beginning of a connection to determine server characteristics and capabilities. If an application normally is used through that connector, train the firewall that way, too. That enables those initial statements to become part of the whitelist for the account associated with the application.

  5. Use the stored procedure to switch the registered user to protecting mode:

    mysql> CALL mysql.sp_set_firewall_mode('fwuser@localhost', 'PROTECTING');
    
    Important

    Switching the account out of RECORDING mode synchronizes its firewall cache data to the underlying mysql system database tables for persistent storage. If you do not switch the mode for a user who is being recorded, the cached whitelist data is not written to the system tables and will be lost when the server is restarted.

  6. Using the registered account, execute some acceptable and unacceptable statements. The firewall matches each one against the account whitelist and accepts or rejects it.

    This statement is not identical to a training statement but produces the same normalized statement as one of them, so the firewall accepts it:

    mysql> SELECT first_name, last_name FROM customer WHERE customer_id = '48';
    +------------+-----------+
    | first_name | last_name |                                           
    +------------+-----------+
    | ANN        | EVANS     |
    +------------+-----------+
    

    These statements do not match anything in the whitelist and each results in an error:

    mysql> SELECT first_name, last_name FROM customer WHERE customer_id = 1 OR TRUE;
    ERROR 1045 (28000): Statement was blocked by Firewall
    mysql> SHOW TABLES LIKE 'customer%';
    ERROR 1045 (28000): Statement was blocked by Firewall
    mysql> TRUNCATE TABLE mysql.slow_log;
    ERROR 1045 (28000): Statement was blocked by Firewall
    

    As of MySQL 5.6.25, the firewall also writes the rejected statements to the error log if the mysql_firewall_trace system variable is enabled. For example:

    [Note] Plugin MYSQL_FIREWALL reported:
    'ACCESS DENIED for fwuser@localhost. Reason: No match in whitelist.
    Statement: TRUNCATE TABLE `mysql` . `slow_log` '
    

    You can use these log messages in your efforts to identify the source of attacks.

  7. As of MySQL 5.6.26, you can log nonmatching statements as suspicious without denying access. To do this, put the account in intrusion-detecting mode:

    mysql> CALL mysql.sp_set_firewall_mode('fwuser@localhost', 'DETECTING');
    
  8. Using the registered account, connect to the server, then execute some statement does not match the whitelist:

    mysql> SHOW TABLES LIKE 'customer%';
    +------------------------------+
    | Tables_in_sakila (customer%) |
    +------------------------------+
    | customer                     |
    | customer_list                |
    +------------------------------+
    

    In detecting mode, the firewall permits the nonmatching statement to execute but writes a message to the error log:

    [Note] Plugin MYSQL_FIREWALL reported:
    'SUSPICIOUS STATEMENT from 'fwuser@localhost'. Reason: No match in whitelist.
    Statement: SHOW TABLES LIKE ? '
    
  9. To assess firewall activity, examine its status variables:

    mysql> SHOW GLOBAL STATUS LIKE 'Firewall%';
    +----------------------------+-------+
    | Variable_name              | Value |
    +----------------------------+-------+
    | Firewall_access_denied     | 3     |
    | Firewall_access_granted    | 4     |
    | Firewall_access_suspicious | 1     |
    | Firewall_cached_entries    | 4     |
    +----------------------------+-------+
    

    The variables indicate the number of statements rejected, accepted, logged as suspicious, and added to the cache, respectively. The Firewall_access_granted count is 4 because of the @@version_comment statement sent by the mysql client each of the three time you used it to connect as the registered user, plus the SHOW TABLES statement that was not blocked in DETECTING mode.

Should additional training for an account be necessary, switch it to recording mode again, then back to protecting mode after executing statements to be added to the whitelist.

7.4.4 MySQL Enterprise Firewall Reference

The following discussion serves as a reference to these MySQL Enterprise Firewall components:

  • Firewall tables in the mysql and INFORMATION_SCHEMA databases

  • Firewall procedures and functions

  • Firewall system variables

  • Firewall status variables

7.4.4.1 MySQL Enterprise Firewall Tables

MySQL Enterprise Firewall maintains account and whitelist information. It uses INFORMATION_SCHEMA tables to provide views into cached data, and tables in the mysql system database to store this data in persistent form. When enabled, the firewall bases its operational decisions on the cached data.

The INFORMATION_SCHEMA tables are accessible by anyone. The mysql tables can be accessed only by users with privileges for that database.

The INFORMATION_SCHEMA.MYSQL_FIREWALL_USERS and mysql.firewall_users tables list registered firewall accounts and their operational modes. The tables have these columns:

  • USERHOST

    An account registered with the firewall. Each account has the format user_name@host_name and represents actual user and host names as authenticated by the server. Patterns and netmasks should not be used when registering users.

  • MODE

    The current firewall operational mode for the account. The permitted mode values are OFF, DETECTING (as of MySQL 5.6.26), PROTECTING, RECORDING, and RESET. For details about their meanings, see the description of sp_set_firewall_mode() in Section 7.4.4.2, “MySQL Enterprise Firewall Procedures and Functions”.

The INFORMATION_SCHEMA.MYSQL_FIREWALL_WHITELIST and mysql.firewall_whitelist tables list registered firewall accounts and their whitelists. The tables have these columns:

  • USERHOST

    An account registered with the firewall. The format is the same as for the user account tables.

  • RULE

    A normalized statement indicating an acceptable statement pattern for the account. An account whitelist is the union of its rules.

7.4.4.2 MySQL Enterprise Firewall Procedures and Functions

MySQL Enterprise Firewall has stored procedures that perform tasks such as registering MySQL accounts with the firewall, establishing their operational mode, and managing transfer of firewall data between the cache and the underlying system tables. It also has a set of user-defined functions (UDFs) that provides an SQL-level API for lower-level tasks such as synchronizing the cache with the underlying system tables.

Under normal operation, the stored procedures implement the user interface. The UDFs are invoked by the stored procedures, not directly by users.

To invoke a stored procedure when the default database is not the database that contains the procedure, qualify the procedure name with the database name. For example:

CALL mysql.sp_set_firewall_mode(user, mode);

The following list describes each firewall stored procedure and UDF:

  • sp_reload_firewall_rules(user)

    This stored procedure uses firewall UDFs to reset a registered account and reload the in-memory rules for it from the rules stored in the mysql.firewall_whitelist table. This procedure provides control over firewall operation for individual accounts.

    The user argument names the affected account, as a string in user_name@host_name format.

    Example:

    CALL mysql.sp_reload_firewall_rules('fwuser@localhost');
    
    Warning

    This procedure sets the account mode to RESET, which clears the account whitelist and sets its mode to OFF. If the account mode was not OFF prior to the sp_reload_firewall_rules() call, use sp_set_firewall_mode() to restore its previous mode after reloading the rules. For example, if the account was in PROTECTING mode, that is no longer true after calling sp_reload_firewall_rules() and you must set it to PROTECTING again explicitly.

  • sp_set_firewall_mode(user, mode)

    This stored procedure registers a MySQL account with the firewall and establishes its operational mode. The procedure also invokes firewall UDFs as necessary to transfer firewall data between the cache and the underlying system tables. This procedure may be called even if the mysql_firewall_mode system variable is OFF, although setting the mode for an account has no operational effect while the firewall is disabled.

    The user argument names the affected account, as a string in user_name@host_name format.

    The mode is the operational mode for the user, as a string. These mode values are permitted:

    • OFF: Disable the firewall for the account.

    • DETECTING: Intrusion-detection mode: Write suspicious (nonmatching) statements to the error log but do not deny access.

    • PROTECTING: Protect the account by matching incoming statements against the account whitelist.

    • RECORDING: Training mode: Record acceptable statements for the account. Incoming statements that do not immediately fail with a syntax error are recorded to become part of the account whitelist rules.

    • RESET: Clear the account whitelist and set the account mode to OFF.

    Switching the mode for an account to any mode but RECORDING synchronizes the firewall cache data to the underlying mysql system database tables for persistent storage. Switching the mode from OFF to RECORDING reloads the whitelist from the mysql.firewall_whitelist table into the cache.

    If an account has an empty whitelist, setting its mode to PROTECTING produces an error message that is returned in a result set, but not an SQL error:

    mysql> CALL mysql.sp_set_firewall_mode('a@b','PROTECTING');
    +----------------------------------------------------------------------+
    | set_firewall_mode(arg_userhost, arg_mode)                            |
    +----------------------------------------------------------------------+
    | ERROR: PROTECTING mode requested for a@b but the whitelist is empty. |
    +----------------------------------------------------------------------+
    1 row in set (0.02 sec)
    Query OK, 0 rows affected (0.02 sec)
    
  • mysql_firewall_flush_status()

    This UDF resets several firewall status variables to 0:

    Firewall_access_denied
    Firewall_access_granted
    Firewall_access_suspicious
    

    Example:

    SELECT mysql_firewall_flush_status();
    
  • normalize_statement(stmt)

    This UDF normalizes an SQL statement into the digest form used for whitelist rules.

    Example:

    SELECT normalize_statement('SELECT * FROM t1 WHERE c1 > 2');
    
  • read_firewall_users(user, mode)

    This aggregate UDF updates the firewall user cache through a SELECT statement on the mysql.firewall_users table.

    Example:

    SELECT read_firewall_users('fwuser@localhost', 'RECORDING')
    FROM mysql.firewall_users;
    
  • read_firewall_whitelist(user, rule)

    This aggregate UDF updates the recorded statement cache through a SELECT statement on the mysql.firewall_whitelist table.

    Example:

    SELECT read_firewall_whitelist('fwuser@localhost', 'RECORDING')
    FROM mysql.firewall_whitelist;
    
  • set_firewall_mode(user, mode)

    This UDF manages the user cache and establishes the user operational mode.

    Example:

    SELECT set_firewall_mode('fwuser@localhost', 'RECORDING');
    

7.4.4.3 MySQL Enterprise Firewall System Variables

MySQL Enterprise Firewall supports the following system variables. Use them to configure firewall operation. These variables are unavailable unless the firewall is installed (see Section 7.4.2, “Installing or Uninstalling MySQL Enterprise Firewall”).

  • mysql_firewall_max_query_size

    Introduced5.6.24
    Removed5.6.26
    Command-Line Format--mysql_firewall_max_query_size=size
    System VariableNamemysql_firewall_max_query_size
    Variable ScopeGlobal
    Dynamic VariableNo
    Permitted ValuesTypeinteger
    Default4096
    Min Value0
    Max Value4294967295

    The maximum size of a normalized statement that can be inserted in the MySQL Enterprise Firewall cache. Normalized statements longer than this size are truncated. Truncated statements are discarded if the firewall mode for the current user is RECORDING and rejected if the mode is PROTECTING.

    mysql_firewall_max_query_size was removed in MySQL 5.6.26. max_digest_length should be set large enough to avoid statement truncation.

  • mysql_firewall_mode

    Introduced5.6.24
    Command-Line Format--mysql_firewall_mode={OFF|ON}
    System VariableNamemysql_firewall_mode
    Variable ScopeGlobal
    Dynamic VariableYes
    Permitted ValuesTypeboolean
    DefaultON

    Whether MySQL Enterprise Firewall is enabled (the default) or disabled.

  • mysql_firewall_trace

    Introduced5.6.24
    Command-Line Format--mysql_firewall_trace={OFF|ON}
    System VariableNamemysql_firewall_trace
    Variable ScopeGlobal
    Dynamic VariableYes
    Permitted ValuesTypeboolean
    DefaultOFF

    Whether the MySQL Enterprise Firewall trace is enabled or disabled (the default). When enabled, mysql_firewall_trace has this effect:

    • In MySQL 5.6.24, the firewall writes a file named firewall_trace.txt in the data directory.

    • In MySQL 5.6.25 and higher, for PROTECTING mode, the firewall writes rejected statements to the error log.

7.4.4.4 MySQL Enterprise Firewall Status Variables

MySQL Enterprise Firewall supports the following status variables. Use them to obtain information about firewall operational status. These variables are unavailable unless the firewall is installed (see Section 7.4.2, “Installing or Uninstalling MySQL Enterprise Firewall”). Firewall status variables are set to 0 whenever the MYSQL_FIREWALL plugin is installed or the server is started. Many of them are reset to zero by the mysql_firewall_flush_status() UDF (see Section 7.4.4.2, “MySQL Enterprise Firewall Procedures and Functions”).