
{"id":2222,"date":"2018-04-04T13:48:29","date_gmt":"2018-04-04T12:48:29","guid":{"rendered":"http:\/\/dasini.net\/blog\/?p=2222"},"modified":"2020-12-08T16:24:42","modified_gmt":"2020-12-08T15:24:42","slug":"mysql-security-mysql-enterprise-audit","status":"publish","type":"post","link":"https:\/\/dasini.net\/blog\/2018\/04\/04\/mysql-security-mysql-enterprise-audit\/","title":{"rendered":"MySQL Security &#8211; MySQL Enterprise Audit"},"content":{"rendered":"<p>When thinking about security within a MySQL installation, you should consider a wide range of possible procedures \/ best practices and how they affect the security of your MySQL server and related applications. MySQL provides many tools \/ features \/ plugins in order to protect your data including some advanced features like <a href=\"http:\/\/dasini.net\/blog\/2018\/04\/10\/mysql-security-mysql-enterprise-transparent-data-encryption\/\" target=\"_blank\" rel=\"noopener noreferrer\">Transparent Data Encryption aka TDE<\/a>, <a href=\"http:\/\/dasini.net\/blog\/2019\/03\/19\/mysql-security-mysql-enterprise-data-masking-and-de-identification\/\" target=\"_blank\" rel=\"noreferrer noopener\" aria-label=\"Data Masking &amp; De-Identification (opens in a new tab)\">Data Masking &amp; De-Identification<\/a>, <a href=\"http:\/\/dasini.net\/blog\/2018\/04\/16\/mysql-security-mysql-enterprise-firewall\/\" target=\"_blank\" rel=\"noopener noreferrer\">Firewall<\/a>, <a href=\"http:\/\/dasini.net\/blog\/2018\/03\/07\/mysql-security-password-management\/\" target=\"_blank\" rel=\"noopener noreferrer\">Password Management<\/a>, <a href=\"http:\/\/dasini.net\/blog\/2018\/03\/01\/mysql-security-password-validation-plugin\/\" target=\"_blank\" rel=\"noopener noreferrer\">Password Validation Plugin<\/a>, etc&#8230;<\/p>\n<p><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" class=\"aligncenter\" src=\"https:\/\/i0.wp.com\/dasini.net\/blog\/wp-content\/uploads\/MySQL_DB_Lock2.png?resize=169%2C179\" alt=\"MySQL Security\" width=\"169\" height=\"179\" \/><\/p>\n<p>In order to spot <strong>database misuse<\/strong> and\/or to prove <strong>compliance<\/strong> to popular regulations including <a href=\"https:\/\/www.eugdpr.org\/\" target=\"_blank\" rel=\"noopener noreferrer\"><strong>GDPR<\/strong><\/a>, <a href=\"https:\/\/en.wikipedia.org\/wiki\/Payment_Card_Industry_Data_Security_Standard\" target=\"_blank\" rel=\"noopener noreferrer\"><strong>PCI DSS<\/strong><\/a>, <a href=\"https:\/\/en.wikipedia.org\/wiki\/Health_Insurance_Portability_and_Accountability_Act\" target=\"_blank\" rel=\"noopener noreferrer\"><strong>HIPAA<\/strong><\/a>, &#8230; database administrators can be required to record and audit database activities. In this fifth episode of the\u00a0<strong>MySQL\u00a0 Security<\/strong> series, we will see what <a href=\"https:\/\/www.mysql.com\/products\/enterprise\/audit.html\" target=\"_blank\" rel=\"noopener noreferrer\">MySQL Enterprise Audit<\/a> provides to help organizations implement stronger security controls and satisfy regulatory compliance.<\/p>\n<h2>MySQL Enterprise Audit<\/h2>\n<p>MySQL Enterprise Edition includes MySQL Enterprise Audit which uses the open MySQL Audit API to enable standard, policy-based monitoring, logging, and blocking of connection and query activity executed on specific MySQL servers.<\/p>\n<p>Audit plugin enables MySQL Server to produce a XML (default) or JSON log file (named <em>audit.log<\/em>) 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.<\/p>\n<p><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" class=\"aligncenter\" src=\"https:\/\/i0.wp.com\/dasini.net\/blog\/wp-content\/uploads\/mysql_enterprise_audit.png?resize=600%2C260\" alt=\"MySQL Enterprise Audit\" width=\"600\" height=\"260\" \/><\/p>\n<h3>Installation<\/h3>\n<p>I&rsquo;m using <strong>MySQL 5.7.21 Enterprise Edition<\/strong> :<\/p>\n<pre class=\"lang:mysql decode:true\" title=\"MySQL Enterprise version\">SELECT VERSION();\n+-------------------------------------------+\n| VERSION()                                 |\n+-------------------------------------------+\n| 5.7.21-enterprise-commercial-advanced-log |\n+-------------------------------------------+<\/pre>\n<p><em>Updated on 22nd of August 2018<span style=\"text-decoration: underline;\"><br \/>Note<\/span>: MySQL Enterprise Audit\u00a0<span style=\"color: #ff0000;\"><strong>works with MySQL 8.0<\/strong><\/span> as well. In other words examples below could be done with <strong>MySQL 8.0.12<\/strong>+<\/em><\/p>\n<p>To install MySQL Enterprise Audit, look in the share directory of your MySQL installation and choose the script that is appropriate for your platform :<\/p>\n<p>&#8211; <strong>audit_log_filter_win_install.sql<\/strong> : Choose this script for Windows systems that use .dll as the file name suffix.<\/p>\n<p>&#8211; <strong>audit_log_filter_linux_install.sql<\/strong> : Choose this script for Linux and similar systems that use .so as the file name suffix.<\/p>\n<p>Then run the script :<\/p>\n<p>e.g.<\/p>\n<pre class=\"lang:sh decode:true\" title=\"Install MySQL Enterprise Audit\">$ mysql -u root -p &lt; audit_log_filter_linux_install.sql<\/pre>\n<p>You can verify the plugin installation examining the <strong>INFORMATION_SCHEMA.PLUGINS<\/strong> table or use the <strong>SHOW PLUGINS<\/strong> statement<\/p>\n<pre class=\"lang:mysql decode:true\" title=\"Check Audit plugin installation\">mysql&gt; \nSELECT PLUGIN_NAME, PLUGIN_STATUS \nFROM INFORMATION_SCHEMA.PLUGINS \nWHERE PLUGIN_NAME LIKE 'audit%';\n+-------------+---------------+\n| PLUGIN_NAME | PLUGIN_STATUS |\n+-------------+---------------+\n| audit_log   | ACTIVE        |\n+-------------+---------------+<\/pre>\n<pre class=\"lang:mysql decode:true\" title=\"SHOW PLUGINS\">mysql&gt; \nSHOW PLUGINS\\G\n... [snip] ...\n*************************** 45. row ***************************\n   Name: audit_log\n Status: ACTIVE\n   Type: AUDIT\nLibrary: audit_log.so\nLicense: PROPRIETARY\n<\/pre>\n<p>The Audit plugin installed a bunch of User-Defined Function (UDF) :<\/p>\n<pre class=\"lang:mysql decode:true\" title=\"Display all the UDF installed by the Audit plugin\">mysql&gt; \n-- Display all the UDFs installed by the Audit plugin\nSELECT * FROM  mysql.func;\n+-----------------------------------+-----+--------------+----------+\n| name                              | ret | dl           | type     |\n+-----------------------------------+-----+--------------+----------+\n| audit_log_filter_set_filter       |   0 | audit_log.so | function |\n| audit_log_filter_remove_filter    |   0 | audit_log.so | function |\n| audit_log_filter_set_user         |   0 | audit_log.so | function |\n| audit_log_filter_remove_user      |   0 | audit_log.so | function |\n| audit_log_filter_flush            |   0 | audit_log.so | function |\n| audit_log_read_bookmark           |   0 | audit_log.so | function |\n| audit_log_read                    |   0 | audit_log.so | function |\n| audit_log_encryption_password_set |   2 | audit_log.so | function |\n| audit_log_encryption_password_get |   0 | audit_log.so | function |\n+-----------------------------------+-----+--------------+----------+<\/pre>\n<p>These functions are described <a href=\"https:\/\/dev.mysql.com\/doc\/mysql-security-excerpt\/5.7\/en\/audit-log-routines.html\" target=\"_blank\" rel=\"noopener noreferrer\">here<\/a>.<\/p>\n<p>The Audit plugin enables MySQL Server to produce a log file based in the <em>datadir<\/em>\u00a0and named <strong><em>audit.log<\/em><\/strong>. By default, its contents is written in XML format, without compression nor encryption :<\/p>\n<pre class=\"lang:mysql decode:true\" title=\"Looking for the MySQL data dir\">mysql&gt; \nSHOW VARIABLES LIKE 'datadir';\n+---------------+-----------------+\n| Variable_name | Value           |\n+---------------+-----------------+\n| datadir       | \/var\/lib\/mysql\/ |\n+---------------+-----------------+<\/pre>\n<pre class=\"lang:xhtml decode:true\" title=\"Audit log content\">$ cat \/var\/lib\/mysql\/audit.log\n&lt;?xml version=\"1.0\" encoding=\"UTF-8\"?&gt;\n&lt;AUDIT&gt;\n &lt;AUDIT_RECORD&gt;\n  &lt;TIMESTAMP&gt;2018-03-28T13:42:01 UTC&lt;\/TIMESTAMP&gt;\n  &lt;RECORD_ID&gt;1_2018-03-28T13:42:01&lt;\/RECORD_ID&gt;\n  &lt;NAME&gt;Audit&lt;\/NAME&gt;\n  &lt;SERVER_ID&gt;0&lt;\/SERVER_ID&gt;\n  &lt;VERSION&gt;1&lt;\/VERSION&gt;\n  &lt;STARTUP_OPTIONS&gt;mysqld&lt;\/STARTUP_OPTIONS&gt;\n  &lt;OS_VERSION&gt;x86_64-linux-glibc2.12&lt;\/OS_VERSION&gt;\n  &lt;MYSQL_VERSION&gt;5.7.21-enterprise-commercial-advanced&lt;\/MYSQL_VERSION&gt;\n &lt;\/AUDIT_RECORD&gt;<\/pre>\n<h3>Configuration<\/h3>\n<p>After MySQL Enterprise Audit is installed, you can use the <a href=\"https:\/\/dev.mysql.com\/doc\/refman\/5.7\/en\/audit-log-reference.html#option_mysqld_audit-log\" target=\"_blank\" rel=\"noopener noreferrer\"><em><strong>audit_log<\/strong><\/em><\/a> option for subsequent server startups to control the Audit plugin activation.<\/p>\n<p>e.g. Prevent the plugin from being removed at runtime, select JSON as logging format and force log file rotation when it reaches 1MB :<\/p>\n<pre class=\"lang:vim decode:true\" title=\"Audit log extra configuration\">[mysqld]\naudit_log=FORCE_PLUS_PERMANENT\naudit_log_format=JSON\naudit_log_rotate_on_size=1048576<\/pre>\n<p>Then restart the MySQL server.<\/p>\n<pre class=\"lang:js decode:true\" title=\"JSON Audit log content\">$ cat \/var\/lib\/mysql\/audit.log\n[  \n   {  \n      \"timestamp\":\"2018-03-28 14:54:27\",\n      \"id\":0,\n      \"class\":\"audit\",\n      \"event\":\"startup\",\n      \"connection_id\":0,\n      \"startup_data\":{  \n         \"server_id\":0,\n         \"os_version\":\"x86_64-linux-glibc2.12\",\n         \"mysql_version\":\"5.7.21-enterprise-commercial-advanced\",\n         \"args\":[  \n            \"mysqld\"\n         ]\n      }\n   }<\/pre>\n<p>We have now a JSON format audit log file.<\/p>\n<p>As you can see, by default, contents of audit log files produced by the audit log plugin are not encrypted and may contain sensitive information, such as the text of SQL statements.\u00a0For security reasons, audit log files should be written to a directory accessible <span style=\"text-decoration: underline;\">only to the MySQL server<\/span> and to users with a <span style=\"text-decoration: underline;\">legitimate reason<\/span> to view the log.<\/p>\n<p>For additional security, you can enable <a href=\"https:\/\/dev.mysql.com\/doc\/mysql-security-excerpt\/5.7\/en\/audit-log-logging-control.html#audit-log-file-encryption\" target=\"_blank\" rel=\"noopener noreferrer\"><strong>audit log file encryption<\/strong><\/a>.<\/p>\n<h3>Encryption<\/h3>\n<p>To encrypt your audit log file, the <a href=\"https:\/\/dev.mysql.com\/doc\/mysql-security-excerpt\/5.7\/en\/keyring.html\" target=\"_blank\" rel=\"noopener noreferrer\">MySQL keyring<\/a> must be enabled because audit logging uses it for password storage. Note that any keyring plugin can be used e.g. :\u00a0<a href=\"https:\/\/dev.mysql.com\/doc\/mysql-security-excerpt\/5.7\/en\/keyring-file-plugin.html\" target=\"_blank\" rel=\"noopener noreferrer\">file-based<\/a>, <a href=\"https:\/\/dev.mysql.com\/doc\/mysql-security-excerpt\/5.7\/en\/keyring-encrypted-file-plugin.html\" target=\"_blank\" rel=\"noopener noreferrer\">encrypted file<\/a>, <a href=\"https:\/\/dev.mysql.com\/doc\/mysql-security-excerpt\/5.7\/en\/keyring-okv-plugin.html\" target=\"_blank\" rel=\"noopener noreferrer\">OKV KMIP compatible<\/a>, <a href=\"https:\/\/dev.mysql.com\/doc\/mysql-security-excerpt\/5.7\/en\/keyring-aws-plugin.html\" target=\"_blank\" rel=\"noopener noreferrer\">AWS<\/a>, &#8230;<\/p>\n<p>To control whether audit log file encryption is enabled, set the <a href=\"https:\/\/dev.mysql.com\/doc\/mysql-security-excerpt\/5.7\/en\/audit-log-options-variables.html#sysvar_audit_log_encryption\" target=\"_blank\" rel=\"noopener noreferrer\">audit_log_encryption<\/a> system variable at server startup. Permitted values are <strong>NONE<\/strong> (no encryption; the default) and <strong>AES<\/strong> (AES-256-CBC cipher encryption).<\/p>\n<p>To set or get the encryption password, use these user-defined functions (UDFs):<\/p>\n<ul>\n<li>To set the encryption password, invoke <strong>audit_log_encryption_password_set(<\/strong>), which stores the password in the keyring, renames the current log file, and begins a new log file encrypted with the new password.<\/li>\n<li>To get the current encryption password, invoke <strong>audit_log_encryption_password_get()<\/strong>, which retrieves the password from the keyring.<\/li>\n<\/ul>\n<p>More details <a href=\"https:\/\/dev.mysql.com\/doc\/mysql-security-excerpt\/5.7\/en\/audit-log-logging-control.html#audit-log-file-encryption\" target=\"_blank\" rel=\"noopener noreferrer\">here<\/a>.<\/p>\n<p>Let&rsquo;s practice!<\/p>\n<h4>Keyring Plugin Installation :\u00a0keyring_encrypted_file<\/h4>\n<p>Like I stated before many keyring plugins are <a href=\"https:\/\/dev.mysql.com\/doc\/mysql-security-excerpt\/5.7\/en\/keyring.html\" target=\"_blank\" rel=\"noopener noreferrer\">available<\/a>. I&rsquo;ll use the keyring plugin : <strong><a href=\"https:\/\/dev.mysql.com\/doc\/mysql-security-excerpt\/5.7\/en\/keyring-encrypted-file-plugin.html\" target=\"_blank\" rel=\"noopener noreferrer\">keyring_encrypted_file<\/a><\/strong>, that stores keyring data in an encrypted file local to the server host.<\/p>\n<p><em><strong><span style=\"text-decoration: underline;\">Warning<\/span><\/strong><\/em><br \/><em>The keyring_encrypted_file plugin for encryption key management is not intended as a regulatory compliance solution. Security standards such as PCI, FIPS, and others require use of key management systems to secure, manage, and protect encryption keys in key vaults or hardware security modules (HSMs).<\/em><\/p>\n<p>Update the MySQL configuration file and restart the MySQL server :<\/p>\n<pre class=\"lang:vim decode:true\" title=\"install keyring plugin\">[mysqld]\naudit_log=FORCE_PLUS_PERMANENT\naudit_log_format=JSON\naudit_log_rotate_on_size=1048576\n\naudit_log_encryption=AES\n\nearly-plugin-load=keyring_encrypted_file.so\nkeyring_encrypted_file_data=\/var\/lib\/mysql\/keyring\/keyring-encrypted\nkeyring_encrypted_file_password=myS3cureP4ssw%rd<\/pre>\n<ul>\n<li>To be usable during the server startup process, <a href=\"https:\/\/dev.mysql.com\/doc\/refman\/5.7\/en\/keyring-encrypted-file-plugin.html\" target=\"_blank\" rel=\"noopener noreferrer\"><strong>keyring_encrypted_file<\/strong><\/a> must be loaded using the <strong><a href=\"https:\/\/dev.mysql.com\/doc\/refman\/5.7\/en\/server-options.html#option_mysqld_early-plugin-load\" target=\"_blank\" rel=\"noopener noreferrer\">early-plugin-load<\/a><\/strong> option.<\/li>\n<li>To specify the password for encrypting the keyring data file, set the <a href=\"https:\/\/dev.mysql.com\/doc\/refman\/5.7\/en\/keyring-system-variables.html#sysvar_keyring_encrypted_file_password\" target=\"_blank\" rel=\"noopener noreferrer\"><strong>keyring_encrypted_file_password<\/strong><\/a> system variable.<\/li>\n<li>The <a href=\"https:\/\/dev.mysql.com\/doc\/refman\/5.7\/en\/keyring-system-variables.html#sysvar_keyring_encrypted_file_data\" target=\"_blank\" rel=\"noopener noreferrer\"><strong>keyring_encrypted_file_data<\/strong><\/a> system variable optionally configures the location of the file used by the keyring_encrypted_file plugin for data storage.<\/li>\n<li>Set the <strong><a href=\"https:\/\/dev.mysql.com\/doc\/mysql-security-excerpt\/5.7\/en\/audit-log-options-variables.html#sysvar_audit_log_encryption\" target=\"_blank\" rel=\"noopener noreferrer\">audit_log_encryption<\/a><\/strong> system variable to <a href=\"https:\/\/fr.wikipedia.org\/wiki\/Advanced_Encryption_Standard\" target=\"_blank\" rel=\"noopener noreferrer\"><strong>AES<\/strong><\/a>.<\/li>\n<\/ul>\n<p><em><strong><span style=\"text-decoration: underline;\">Warning<\/span><\/strong><\/em><br \/><em>Because the my.cnf file stores a password when written as shown, it should have a restrictive mode and be accessible only to the account used to run the MySQL server.<\/em><\/p>\n<pre class=\"lang:mysql decode:true\" title=\"keyring_encrypted_file details\">mysql&gt; \nSELECT PLUGIN_NAME, PLUGIN_STATUS, PLUGIN_LIBRARY, PLUGIN_DESCRIPTION \nFROM INFORMATION_SCHEMA.PLUGINS \nWHERE PLUGIN_NAME = 'keyring_encrypted_file'\\G\n*************************** 1. row ***************************\n       PLUGIN_NAME: keyring_encrypted_file\n     PLUGIN_STATUS: ACTIVE\n    PLUGIN_LIBRARY: keyring_encrypted_file.so\nPLUGIN_DESCRIPTION: store\/fetch authentication data to\/from an encrypted file\n\n<\/pre>\n<p>Now the default name of Audit log file is <strong>audit.log.enc<\/strong> instead of <strong>audit.log<\/strong>.<br \/>And obviously this is not the only change. Guess what? this new file is&#8230; encrypted (AES-256-CBC cipher encryption)<\/p>\n<pre class=\"lang:sh decode:true\" title=\"audit.log.enc content\">$ strings  '\/var\/lib\/mysql\/audit.log.enc' \nSalted__\n9,]`&lt;Lrc}P\nd.Zw\n!U\\?\nv_l%\nOq}{\/\nd_qa\nS7C0\n...<\/pre>\n<p>If you don&rsquo;t want to use encryption anymore you&rsquo;ll need to update <em>audit_log_encryption<\/em> option in your\u00a0MySQL configuration file.<br \/>e.g.<\/p>\n<pre class=\"lang:vim decode:true\" title=\"audit_log_encryption=NONE\">[mysqld]\naudit_log_encryption=NONE<\/pre>\n<p>Then restart the MySQL server<\/p>\n<h3>Compression<\/h3>\n<p>You can lowering the disk footprint of your audit logs with compression.<\/p>\n<p>To control whether audit log file compression is enabled, set the <a href=\"https:\/\/dev.mysql.com\/doc\/mysql-security-excerpt\/5.7\/en\/audit-log-options-variables.html#sysvar_audit_log_compression\" target=\"_blank\" rel=\"noopener noreferrer\">audit_log_compression<\/a> system variable at server startup.<\/p>\n<p>Permitted values are <strong>NONE<\/strong> (no compression; the default) and <strong>GZIP<\/strong> (GNU Zip compression).<\/p>\n<p>More details <a href=\"https:\/\/dev.mysql.com\/doc\/mysql-security-excerpt\/5.7\/en\/audit-log-logging-control.html#audit-log-file-uncompression-decryption\" target=\"_blank\" rel=\"noopener noreferrer\">here<\/a>.<\/p>\n<p>Let&rsquo;s practice!<\/p>\n<pre class=\"lang:mysql decode:true\" title=\"audit_log_compression=NONE\">SHOW VARIABLES LIKE 'audit_log_compression';\n+-----------------------+-------+\n| Variable_name         | Value |\n+-----------------------+-------+\n| audit_log_compression | NONE  |\n+-----------------------+-------+<\/pre>\n<p>Update the MySQL server configuration file :<\/p>\n<pre class=\"lang:vim decode:true\" title=\"audit_log_compression=GZIP\">[mysqld]\naudit_log=FORCE_PLUS_PERMANENT\naudit_log_format=JSON\naudit_log_rotate_on_size=1048576\n\naudit_log_compression=GZIP\n<\/pre>\n<p>Then restart the server :<\/p>\n<pre class=\"lang:mysql decode:true\" title=\"Audit log compressed\">SHOW VARIABLES LIKE 'audit_log_compression';\n+-----------------------+-------+\n| Variable_name         | Value |\n+-----------------------+-------+\n| audit_log_compression | GZIP  |\n+-----------------------+-------+<\/pre>\n<p>Now the default name of Audit log file is <strong>audit.log.gz<\/strong> instead of <strong>audit.log<\/strong>.<br \/>This new file is compressed with <strong><a href=\"http:\/\/www.gzip.org\/\" target=\"_blank\" rel=\"noopener noreferrer\">GNU Zip<\/a><\/strong>.<\/p>\n<pre class=\"lang:mysql decode:true\" title=\"Different audit log formats\">$ ls -l audit.*\n... 832 Apr  3 14:38 audit.20180403T123833.log.enc\n... 803 Apr  3 14:49 audit.20180403T124958.log\n...  20 Apr  3 14:50 audit.log.gz<\/pre>\n<p><em><strong><span style=\"text-decoration: underline;\">Note<\/span><\/strong><\/em><br \/><em>If both <strong>compression<\/strong> and <strong>encryption<\/strong> are enabled, compression occurs <span style=\"text-decoration: underline;\">before<\/span> encryption. To recover the original file manually, first decrypt it, then uncompress it.<\/em><\/p>\n<p>If you don&rsquo;t want to use compression anymore you&rsquo;ll need to update <em>audit_log_compression<\/em> option in your\u00a0MySQL configuration file.<br \/>e.g.<\/p>\n<pre class=\"lang:vim decode:true\" title=\"audit_log_compression=NONE\">[mysqld]\naudit_log_compression=NONE<\/pre>\n<p>Then restart the MySQL server<\/p>\n<h3>Audit Log Filtering<\/h3>\n<p>Another amazing feature is the\u00a0audit log filtering functions. It enables filtering control in JSON format by providing an interface to create, modify, and remove filter definitions and assign filters to user accounts.<\/p>\n<p>When a connection arrives, the audit log plugin determines which filter to use for the new session by searching for the user account name in the current filter assignments:<\/p>\n<ul>\n<li>If a filter is assigned to the user, the audit log uses that filter.<\/li>\n<li>Otherwise, if no user-specific filter assignment exists, but there is a filter assigned to the default account (%), the audit log uses the default filter.<\/li>\n<li>Otherwise, the audit log selects no audit events from the session for processing.<\/li>\n<\/ul>\n<p>By default, no accounts have a filter assigned, so no processing of auditable events occurs for any account.<\/p>\n<p>The following list briefly summarizes the UDFs that implement the SQL interface for audit filtering control:<\/p>\n<ul>\n<li><strong>audit_log_filter_set_filter<\/strong>() : Define a filter<\/li>\n<li><strong>audit_log_filter_remove_filter<\/strong>() : Remove a filter<\/li>\n<li><strong>audit_log_filter_set_user<\/strong>() : Start filtering a user account<\/li>\n<li><strong>audit_log_filter_remove_user<\/strong>() : Stop filtering a user account<\/li>\n<li><strong>audit_log_filter_flush<\/strong>() : Flush manual changes to the filter tables to affect ongoing filtering<\/li>\n<\/ul>\n<p>Detailed description <a href=\"https:\/\/dev.mysql.com\/doc\/mysql-security-excerpt\/5.7\/en\/audit-log-routines.html\" target=\"_blank\" rel=\"noopener noreferrer\">here<\/a>.<\/p>\n<h4>Examples<\/h4>\n<p>Users and\u00a0Filters can be monitored respectively with <strong>mysql.audit_log_user<\/strong> and\u00a0<strong>mysql.audit_log_filter<\/strong> tables<\/p>\n<p>e.g.<\/p>\n<pre class=\"lang:mysql decode:true\">mysql&gt; \nSELECT * FROM mysql.audit_log_user\\G\n*************************** 1. row ***************************\n      USER: %\n      HOST: \nFILTERNAME: log_all\n\n\nSELECT * FROM mysql.audit_log_filter\\G\n*************************** 1. row ***************************\n  NAME: log_all\nFILTER: {\"filter\": {\"log\": true}}<\/pre>\n<p><span style=\"text-decoration: underline;\"><strong>Logging All Events<\/strong><\/span><\/p>\n<p>You can log all auditable events for all users by create a simple filter to enable logging and assign it to the default account:<\/p>\n<pre class=\"lang:mysql decode:true\">mysql&gt;\nSELECT audit_log_filter_set_filter('log_all', '{ \"filter\": { \"log\": true } }');\n+-------------------------------------------------------------------------+\n| audit_log_filter_set_filter('log_all', '{ \"filter\": { \"log\": true } }') |\n+-------------------------------------------------------------------------+\n| OK                                                                      |\n+-------------------------------------------------------------------------+\n\n\nSELECT audit_log_filter_set_user('%', 'log_all');\n+-------------------------------------------+\n| audit_log_filter_set_user('%', 'log_all') |\n+-------------------------------------------+\n| OK                                        |\n+-------------------------------------------+<\/pre>\n<p>The filter assigned to % is used for connections from any account that has no explicitly assigned filter (which initially is true for all accounts).<\/p>\n<p>To determine whether a filter has been assigned to the current session, check the session value of the read-only <a href=\"https:\/\/dev.mysql.com\/doc\/refman\/5.7\/en\/audit-log-reference.html#sysvar_audit_log_filter_id\" target=\"_blank\" rel=\"noopener noreferrer\"><strong>audit_log_filter_id<\/strong><\/a> system variable. If the value is <strong>0<\/strong>, no filter is assigned. A nonzero value indicates the internally maintained ID of the assigned filter:<\/p>\n<pre class=\"lang:mysql decode:true\" title=\"Check if a filter has been assigned to the current session\">mysql&gt; \n-- Check if a filter has been assigned to the current session\nSELECT @@audit_log_filter_id;\n+-----------------------+\n| @@audit_log_filter_id |\n+-----------------------+\n|                     0 |\n+-----------------------+\n\n-- Re-connection\nconnect\nConnection id:    7\nCurrent database: *** NONE ***\n\nSELECT @@audit_log_filter_id;\n+-----------------------+\n| @@audit_log_filter_id |\n+-----------------------+\n|                     1 |\n+-----------------------+\n\n\n-- Execute a query of your choice e.g.\nSHOW SCHEMAS;\n+--------------------+\n| Database           |\n+--------------------+\n| information_schema |\n| drupal             |\n... [snip] ...\n\n\n-- Reads the most recently written audit log event from the audit log\nSELECT audit_log_read(audit_log_read_bookmark())\\G\n*************************** 1. row ***************************\naudit_log_read(audit_log_read_bookmark()): [ {\"timestamp\":\"2018-04-03 15:34:11\",\"id\":0,\"class\":\"general\",\"event\":\"status\",\"connection_id\":7,\"account\":{\"user\":\"root\",\"host\":\"localhost\"},\"login\":{\"user\":\"root\",\"os\":\"\",\"ip\":\"\",\"proxy\":\"\"},\"general_data\":{\"command\":\"Query\",\"sql_command\":\"show_databases\",\"query\":\"SHOW SCHEMAS\",\"status\":0}}, null ]\n<\/pre>\n<p><em><span style=\"text-decoration: underline;\">Note<\/span><\/em><br \/><em>Starting with MySQL <strong>5.7.22<\/strong>, you can use <strong><a href=\"https:\/\/dev.mysql.com\/doc\/refman\/5.7\/en\/json-utility-functions.html#function_json-pretty\" target=\"_blank\" rel=\"noopener noreferrer\">JSON_PRETTY<\/a><\/strong> function that\u00a0prints out a JSON value in a format that is easy to read.<\/em><\/p>\n<p><em>Updated on 22nd of August 2018<span style=\"text-decoration: underline;\"><br \/><\/span><\/em>For convenience, a <strong>JSON_PRETTY<\/strong> example with <strong>MySQL 8.0.12<\/strong>:<\/p>\n<pre class=\"lang:mysql decode:true \" title=\"JSON_PRETTY with audit_log_read &amp; audit_log_read_bookmark usage example\">mysql&gt;\nSELECT VERSION();\n+-------------------+\n| VERSION()         |\n+-------------------+\n| 8.0.12-commercial |\n+-------------------+\n\n\nSELECT JSON_PRETTY(CONVERT(audit_log_read(audit_log_read_bookmark()) using utf8mb4))\\G\n*************************** 1. row ***************************\nJSON_PRETTY(CONVERT(audit_log_read(audit_log_read_bookmark()) using utf8mb4)): [\n  {\n    \"id\": 0,\n    \"class\": \"general\",\n    \"event\": \"status\",\n    \"login\": {\n      \"ip\": \"\",\n      \"os\": \"\",\n      \"user\": \"msandbox\",\n      \"proxy\": \"\"\n    },\n    \"account\": {\n      \"host\": \"localhost\",\n      \"user\": \"msandbox\"\n    },\n    \"timestamp\": \"2018-08-22 13:19:54\",\n    \"general_data\": {\n      \"query\": \"SELECT CONVERT(audit_log_read(audit_log_read_bookmark()), JSON)\",\n      \"status\": 0,\n      \"command\": \"Query\",\n      \"sql_command\": \"select\"\n    },\n    \"connection_id\": 12\n  },\n  null\n]<\/pre>\n<p><span style=\"text-decoration: underline;\"><strong>Remove a filter on a user account<\/strong><\/span><\/p>\n<p>I don&rsquo;t want to audit all users anymore. Instead I&rsquo;ll use the log_all filter to audit the user account olivier@%.<\/p>\n<pre class=\"lang:mysql decode:true\" title=\"Remove a filter on a user account\">-- Stop auditing all users \nSELECT audit_log_filter_remove_user('%');\n+-----------------------------------+\n| audit_log_filter_remove_user('%') |\n+-----------------------------------+\n| OK                                |\n+-----------------------------------+\n\n\nSELECT audit_log_read(audit_log_read_bookmark())\\G\n*************************** 1. row ***************************\naudit_log_read(audit_log_read_bookmark()): [ {\"timestamp\":\"2018-04-03 15:48:08\",\"id\":0,\"class\":\"general\",\"event\":\"status\",\"connection_id\":7,\"account\":{\"user\":\"root\",\"host\":\"localhost\"},\"login\":{\"user\":\"root\",\"os\":\"\",\"ip\":\"\",\"proxy\":\"\"},\"general_data\":{\"command\":\"Query\",\"sql_command\":\"select\",\"query\":\"SELECT audit_log_filter_remove_user('%')\",\"status\":0}}, null ]\n\n\n-- Audit still enabled for the current session\nSELECT 1;\n+---+\n| 1 |\n+---+\n| 1 |\n+---+\n\n\nSELECT audit_log_read(audit_log_read_bookmark())\\G\n*************************** 1. row ***************************\naudit_log_read(audit_log_read_bookmark()): [ {\"timestamp\":\"2018-04-03 15:50:42\",\"id\":0,\"class\":\"general\",\"event\":\"status\",\"connection_id\":7,\"account\":{\"user\":\"root\",\"host\":\"localhost\"},\"login\":{\"user\":\"root\",\"os\":\"\",\"ip\":\"\",\"proxy\":\"\"},\"general_data\":{\"command\":\"Query\",\"sql_command\":\"select\",\"query\":\"SELECT 1\",\"status\":0}}, null ]\n\n\n-- Reset the session\nconnect\nConnection id:    10\nCurrent database: *** NONE ***\n\n\nSELECT 2;\n+---+\n| 2 |\n+---+\n| 2 |\n+---+\n\n-- No more entries in the audit log\nSELECT audit_log_read(audit_log_read_bookmark())\\G\n*************************** 1. row ***************************\naudit_log_read(audit_log_read_bookmark()): [ {\"timestamp\":\"2018-04-03 15:50:49\",\"id\":0,\"class\":\"connection\",\"event\":\"disconnect\",\"connection_id\":7,\"account\":{\"user\":\"root\",\"host\":\"localhost\"},\"login\":{\"user\":\"root\",\"os\":\"\",\"ip\":\"\",\"proxy\":\"\"},\"connection_data\":{\"connection_type\":\"socket\"}}, null ]<\/pre>\n<p><em><span style=\"text-decoration: underline;\">Note<\/span><\/em><br \/><em>Filtering of current sessions for the user remains unaffected.\u00a0<\/em><\/p>\n<p><span style=\"text-decoration: underline;\"><strong>Assign a filter explicitly to a particular user account<\/strong><\/span><\/p>\n<p>The filter is already defined (log_all), let&rsquo;s assign it to the olivier@% user account :<\/p>\n<pre class=\"lang:mysql decode:true\" title=\"Assign a filter explicitly to a particular user account \">mysql_root&gt; \nSELECT audit_log_filter_set_user('olivier@%', 'log_all');\n+---------------------------------------------------+\n| audit_log_filter_set_user('olivier@%', 'log_all') |\n+---------------------------------------------------+\n| OK                                                |\n+---------------------------------------------------+\n<\/pre>\n<p>In another session connection with user olivier :<\/p>\n<pre class=\"lang:mysql decode:true\" title=\"Connection with user olivier\">$ mysql -uolivier -p\n<\/pre>\n<p>All commands from user olivier are audited :<\/p>\n<pre class=\"lang:mysql decode:true\" title=\"filter for a particular user account\">mysql_root&gt;\n-- Root session\nSELECT audit_log_read(audit_log_read_bookmark())\\G\n*************************** 1. row ***************************\naudit_log_read(audit_log_read_bookmark()): [ {\"timestamp\":\"2018-04-03 16:01:09\",\"id\":0,\"class\":\"connection\",\"event\":\"connect\",\"connection_id\":15,\"account\":{\"user\":\"olivier\",\"host\":\"localhost\"},\"login\":{\"user\":\"olivier\",\"os\":\"\",\"ip\":\"\",\"proxy\":\"\"},\"connection_data\":{\"connection_type\":\"socket\",\"status\":0,\"db\":\"\"}}, {\"timestamp\":\"2018-04-03 16:01:09\",\"id\":1,\"class\":\"general\",\"event\":\"status\",\"connection_id\":15,\"account\":{\"user\":\"olivier\",\"host\":\"localhost\"},\"login\":{\"user\":\"olivier\",\"os\":\"\",\"ip\":\"\",\"proxy\":\"\"},\"general_data\":{\"command\":\"Query\",\"sql_command\":\"select\",\"query\":\"select @@version_comment limit 1\",\"status\":0}}, {\"timestamp\":\"2018-04-03 16:01:09\",\"id\":2,\"class\":\"general\",\"event\":\"status\",\"connection_id\":15,\"account\":{\"user\":\"olivier\",\"host\":\"localhost\"},\"login\":{\"user\":\"olivier\",\"os\":\"\",\"ip\":\"\",\"proxy\":\"\"},\"general_data\":{\"command\":\"Query\",\"sql_command\":\"select\",\"query\":\"select USER()\",\"status\":0}}, null ]\n\n\n-- Olivier session\nmysql_olivier&gt;\nSELECT 'Olivier';\n+---------+\n| Olivier |\n+---------+\n| Olivier |\n+---------+\n\n\n-- Back to root session\nmysql_root&gt;\nSELECT audit_log_read(audit_log_read_bookmark())\\G\n*************************** 1. row ***************************\naudit_log_read(audit_log_read_bookmark()): [ {\"timestamp\":\"2018-04-03 16:17:17\",\"id\":0,\"class\":\"general\",\"event\":\"status\",\"connection_id\":15,\"account\":{\"user\":\"olivier\",\"host\":\"localhost\"},\"login\":{\"user\":\"olivier\",\"os\":\"\",\"ip\":\"\",\"proxy\":\"\"},\"general_data\":{\"command\":\"Query\",\"sql_command\":\"select\",\"query\":\"SELECT 'Olivier'\",\"status\":0}}, null ]<\/pre>\n<p><span style=\"text-decoration: underline;\"><strong>Logging Specific Event Classes<\/strong><\/span><\/p>\n<p>Create a new filter :\u00a0log_table_access_events<\/p>\n<p>It allows to audit all table accesses. This filter is assigned to user olivier. So it replace any previous filter.<\/p>\n<pre class=\"lang:mysql decode:true \" title=\"New filter\">mysql_root&gt;\nSELECT audit_log_filter_set_filter('log_table_access_events', '{ \"filter\": { \"class\": { \"name\": \"table_access\" } } }');\n+-----------------------------------------------------------------------------------------------------------------+\n| audit_log_filter_set_filter('log_table_access_events', '{ \"filter\": { \"class\": { \"name\": \"table_access\" } } }') |\n+-----------------------------------------------------------------------------------------------------------------+\n| OK                                                                                                              |\n+-----------------------------------------------------------------------------------------------------------------+\n\nSELECT audit_log_filter_set_user('olivier@%', 'log_table_access_events');\n+-------------------------------------------------------------------+\n| audit_log_filter_set_user('olivier@%', 'log_table_access_events') |\n+-------------------------------------------------------------------+\n| OK                                                                |\n+-------------------------------------------------------------------+\n\nSELECT * FROM mysql.audit_log_user WHERE USER='olivier'\\G\n*************************** 1. row ***************************\nUSER: olivier\nHOST: %\nFILTERNAME: log_table_access_events\n\n\nSELECT * FROM mysql.audit_log_filter WHERE NAME='log_table_access_events'\\G\n*************************** 1. row ***************************\nNAME: log_table_access_events\nFILTER: {\"filter\": {\"class\": {\"name\": \"table_access\"}}}<\/pre>\n<p>Connection with user olivier<\/p>\n<pre class=\"lang:sh decode:true\">$ mysql -u olivier -p\n<\/pre>\n<p>Run a query on a table (table access)<\/p>\n<pre class=\"lang:mysql decode:true \" title=\"SELECT * FROM actor LIMIT 1\\G\">-- Olivier's session\nmysql_olivier&gt;\nSELECT * FROM actor LIMIT 1\\G\n*************************** 1. row ***************************\nactor_id: 1\nfirst_name: PENELOPE\nlast_name: GUINESS\nlast_update: 2006-02-15 04:34:33<\/pre>\n<p>This command is logged in Audit log file :<\/p>\n<pre class=\"lang:mysql decode:true \" title=\"SELECT audit_log_read(audit_log_read_bookmark())\\G\">mysql_root&gt;\nSELECT audit_log_read(audit_log_read_bookmark())\\G\n*************************** 1. row ***************************\naudit_log_read(audit_log_read_bookmark()): [ {\"timestamp\":\"2018-04-03 17:03:03\",\"id\":0,\"class\":\"table_access\",\"event\":\"read\",\"connection_id\":25,\"account\":{\"user\":\"olivier\",\"host\":\"localhost\"},\"login\":{\"user\":\"olivier\",\"os\":\"\",\"ip\":\"\",\"proxy\":\"\"},\"table_access_data\":{\"db\":\"sakila\",\"table\":\"actor\",\"query\":\"SELECT * FROM actor LIMIT 1\",\"sql_command\":\"select\"}}, null ]<\/pre>\n<p>Other kinds of queries will not be logged :<\/p>\n<pre class=\"lang:mysql decode:true \" title=\"CREATE SCHEMA s1;\">-- Olivier's session\nmysql_olivier&gt;\nCREATE SCHEMA s1;\nQuery OK, 1 row affected (0,01 sec)<\/pre>\n<p>The Audit log file did not changed<\/p>\n<pre class=\"lang:mysql decode:true\" title=\"SELECT audit_log_read(audit_log_read_bookmark())\\G\">mysql_root&gt;\nSELECT audit_log_read(audit_log_read_bookmark())\\G\n*************************** 1. row ***************************\naudit_log_read(audit_log_read_bookmark()): [ {\"timestamp\":\"2018-04-03 17:03:03\",\"id\":0,\"class\":\"table_access\",\"event\":\"read\",\"connection_id\":25,\"account\":{\"user\":\"olivier\",\"host\":\"localhost\"},\"login\":{\"user\":\"olivier\",\"os\":\"\",\"ip\":\"\",\"proxy\":\"\"},\"table_access_data\":{\"db\":\"sakila\",\"table\":\"actor\",\"query\":\"SELECT * FROM actor LIMIT 1\",\"sql_command\":\"select\"}}, null ]<\/pre>\n<p>It&rsquo;s also possible to select a specific event on these tables like INSERT, DELETE, UPDATE&#8230;<\/p>\n<p>You&rsquo;ll find all the details <a href=\"https:\/\/dev.mysql.com\/doc\/refman\/5.7\/en\/audit-log-filtering.html\" target=\"_blank\" rel=\"noopener noreferrer\">here<\/a>.<\/p>\n<p><span style=\"text-decoration: underline;\"><strong>Blocking Execution of Specific Events<\/strong><\/span><\/p>\n<p>Event items can include an abort item that indicates whether to prevent qualifying events from executing.<\/p>\n<p>This is convenient\u00a0 to block execution of specific SQL statements.<\/p>\n<p>Let&rsquo;s block INSERTs, UPDATEs and DELETEs on sakila.payment table :<\/p>\n<pre class=\"lang:mysql decode:true\" title=\" This filter blocks INSERT, UPDATE, and DELETE statements only for a sakila.payment table\">mysql_root&gt;\n-- This filter blocks INSERT, UPDATE, and DELETE statements only for sakila.payment table\nSET @filter = '{\"filter\": {\"class\": {\"name\": \"table_access\",\"event\": {\"name\": [ \"insert\", \"update\", \"delete\" ],\"abort\": {\"and\": [{ \"field\": { \"name\": \"table_database.str\", \"value\": \"sakila\" } }, { \"field\": { \"name\": \"table_name.str\", \"value\": \"payment\" } }]}}}}}';\nQuery OK, 0 rows affected (0,00 sec)\n\n\nSELECT audit_log_filter_set_filter('block_writes_on_table_payment_events', @filter);\n+------------------------------------------------------------------------------+\n| audit_log_filter_set_filter('block_writes_on_table_payment_events', @filter) |\n+------------------------------------------------------------------------------+\n| OK                                                                           |\n+------------------------------------------------------------------------------+\n\nSELECT audit_log_filter_set_user('olivier@%', 'block_writes_on_table_payment_events');\n+--------------------------------------------------------------------------------+\n| audit_log_filter_set_user('olivier@%', 'block_writes_on_table_payment_events') |\n+--------------------------------------------------------------------------------+\n| OK                                                                             |\n+--------------------------------------------------------------------------------+\n\nSELECT * FROM mysql.audit_log_user WHERE USER='olivier'\\G\n*************************** 1. row ***************************\nUSER: olivier\nHOST: %\nFILTERNAME: block_writes_on_table_payment_events\n\n\nSELECT * FROM mysql.audit_log_filter WHERE NAME='block_writes_on_table_payment_events'\\G\n*************************** 1. row ***************************\nNAME: block_writes_on_table_payment_events\nFILTER: {\"filter\": {\"class\": {\"name\": \"table_access\", \"event\": {\"name\": [\"insert\", \"update\", \"delete\"], \"abort\": {\"and\": [{\"field\": {\"name\": \"table_database.str\", \"value\": \"sakila\"}}, {\"field\": {\"name\": \"table_name.str\", \"value\": \"payment\"}}]}}}}}<\/pre>\n<p>When using user olivier :<\/p>\n<pre class=\"lang:sh decode:true \" title=\"Connection with user olivier\"># Connection with user olivier\n$ mysql -u olivier -p<\/pre>\n<p>Any forbidden queries will be blocked&#8230;<\/p>\n<pre class=\"lang:mysql decode:true\" title=\"-- MySQL Audit plugin prevents writes on sakila.payment\">mysql_olivier&gt;\nSELECT COUNT(*) FROM sakila.payment;\n+----------+\n| COUNT(*) |\n+----------+\n|    16049 |\n+----------+\n\n\n-- MySQL Audit plugin prevents writes on sakila.payment\nDELETE FROM sakila.payment;\nERROR 1045 (28000): Statement was aborted by an audit log filter\n\n\nSELECT COUNT(*) FROM sakila.payment;\n+----------+\n| COUNT(*) |\n+----------+\n|    16049 |\n+----------+\n\n<\/pre>\n<p>and logged by the MySQL Audit plugin.<\/p>\n<pre class=\"lang:mysql decode:true\" title=\"SELECT audit_log_read(audit_log_read_bookmark())\\G\">mysql_root&gt; \nSELECT audit_log_read(audit_log_read_bookmark())\\G\n*************************** 1. row ***************************\naudit_log_read(audit_log_read_bookmark()): [ {\"timestamp\":\"2018-04-03 17:29:22\",\"id\":0,\"class\":\"table_access\",\"event\":\"delete\",\"connection_id\":28,\"account\":{\"user\":\"olivier\",\"host\":\"localhost\"},\"login\":{\"user\":\"olivier\",\"os\":\"\",\"ip\":\"\",\"proxy\":\"\"},\"table_access_data\":{\"db\":\"sakila\",\"table\":\"payment\",\"query\":\"DELETE FROM sakila.payment\",\"sql_command\":\"delete\"}}, null ]\n<\/pre>\n<pre class=\"lang:sh decode:true\" title=\"tail \/var\/lib\/mysql\/audit.log\">$ tail \/var\/lib\/mysql\/audit.log\n{ \"timestamp\": \"2018-04-03 17:28:00\", \"id\": 0, \"class\": \"table_access\", \"event\": \"read\", \"connection_id\": 25, \"account\": { \"user\": \"olivier\", \"host\": \"localhost\" }, \"login\": { \"user\": \"olivier\", \"os\": \"\", \"ip\": \"\", \"proxy\": \"\" }, \"table_access_data\": { \"db\": \"sakila\", \"table\": \"payment\", \"query\": \"SELECT COUNT(*) FROM sakila.payment\", \"sql_command\": \"select\" } },\n{ \"timestamp\": \"2018-04-03 17:29:22\", \"id\": 0, \"class\": \"table_access\", \"event\": \"delete\", \"connection_id\": 28, \"account\": { \"user\": \"olivier\", \"host\": \"localhost\" }, \"login\": { \"user\": \"olivier\", \"os\": \"\", \"ip\": \"\", \"proxy\": \"\" }, \"table_access_data\": { \"db\": \"sakila\", \"table\": \"payment\", \"query\": \"DELETE FROM sakila.payment\", \"sql_command\": \"delete\" } }<\/pre>\n<p>And now Olivier is in big troubles!!! \ud83d\ude42<\/p>\n<p><strong>MySQL Enterprise Audit<\/strong> plugin is very powerful!<\/p>\n<p>Please read the documentation for more information.<\/p>\n<h3>Uninstallation<\/h3>\n<p>To remove MySQL Enterprise Audit, execute the following statements :<\/p>\n<pre class=\"lang:mysql decode:true\" title=\"uninstall MySQL Enterprise Audit\">DROP TABLE IF EXISTS mysql.audit_log_filter;\nDROP TABLE IF EXISTS mysql.audit_log_user;\nUNINSTALL PLUGIN audit_log;\nDROP FUNCTION audit_log_filter_set_filter;\nDROP FUNCTION audit_log_filter_remove_filter;\nDROP FUNCTION audit_log_filter_set_user;\nDROP FUNCTION audit_log_filter_remove_user;\nDROP FUNCTION audit_log_filter_flush;\nDROP FUNCTION audit_log_encryption_password_get;\nDROP FUNCTION audit_log_encryption_password_set;\nDROP FUNCTION audit_log_read;\nDROP FUNCTION audit_log_read_bookmark;<\/pre>\n<p>Voil\u00e0!<\/p>\n<h2>MySQL Enterprise Edition<\/h2>\n<p><a href=\"https:\/\/www.mysql.com\/products\/enterprise\/\" target=\"_blank\" rel=\"noopener noreferrer\">MySQL Enterprise Edition<\/a> includes the most comprehensive set of advanced features, management tools and technical support to achieve the highest levels of MySQL scalability, security, reliability, and uptime.<\/p>\n<p>It reduces the risk, cost, and complexity in developing, deploying, and managing business-critical MySQL applications.<\/p>\n<p><a href=\"https:\/\/edelivery.oracle.com\/\" target=\"_blank\" rel=\"noopener noreferrer\">MySQL Enterprise Edition server\u00a0Trial Download<\/a>\u00a0(Note &#8211; Select Product Pack: MySQL Database).<\/p>\n<p><a href=\"https:\/\/www.mysql.com\/products\/enterprise\/\" target=\"_blank\" rel=\"noopener noreferrer\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" class=\"aligncenter\" src=\"https:\/\/i0.wp.com\/dasini.net\/blog\/wp-content\/uploads\/MySQL_Enterprise_Edition_600x313.png?resize=600%2C313\" alt=\"MySQL Enterprise Edition\" width=\"600\" height=\"313\" \/><\/a><\/p>\n<h2>In order to go further<\/h2>\n<h3>MySQL Security Series<\/h3>\n<ol>\n<li><a href=\"http:\/\/dasini.net\/blog\/2018\/03\/01\/mysql-security-password-validation-plugin\/\" target=\"_blank\" rel=\"noopener noreferrer\">Password Validation Plugin<\/a><\/li>\n<li><a href=\"http:\/\/dasini.net\/blog\/2018\/03\/07\/mysql-security-password-management\/\" target=\"_blank\" rel=\"noopener noreferrer\">Password Management<\/a><\/li>\n<li><a href=\"http:\/\/dasini.net\/blog\/2018\/03\/14\/mysql-security-user-account-locking\/\" target=\"_blank\" rel=\"noopener noreferrer\">User Account Locking<\/a><\/li>\n<li><a href=\"http:\/\/dasini.net\/blog\/2018\/03\/29\/mysql-security-the-connection-control-plugins\/\" target=\"_blank\" rel=\"noopener noreferrer\">The Connection-Control Plugins<\/a><\/li>\n<li><a href=\"http:\/\/dasini.net\/blog\/2018\/04\/04\/mysql-security-mysql-enterprise-audit\/\" target=\"_blank\" rel=\"noopener noreferrer\">Enterprise Audit<\/a><\/li>\n<li><a href=\"http:\/\/dasini.net\/blog\/2018\/04\/10\/mysql-security-mysql-enterprise-transparent-data-encryption\/\" target=\"_blank\" rel=\"noopener noreferrer\">Enterprise Transparent Data Encryption (TDE)<\/a><\/li>\n<li><a href=\"http:\/\/dasini.net\/blog\/2018\/04\/16\/mysql-security-mysql-enterprise-firewall\/\" target=\"_blank\" rel=\"noopener noreferrer\">Enterprise Firewall<\/a><\/li>\n<li><a href=\"http:\/\/dasini.net\/blog\/2019\/03\/19\/mysql-security-mysql-enterprise-data-masking-and-de-identification\/\" target=\"_blank\" rel=\"noreferrer noopener\" aria-label=\"Enterprise Data Masking and De-Identification (opens in a new tab)\">Enterprise Data Masking and De-Identification<\/a><\/li>\n<\/ol>\n<h3>Reference Manual<\/h3>\n<ul>\n<li><a href=\"https:\/\/dev.mysql.com\/doc\/mysql-secure-deployment-guide\/5.7\/en\/secure-deployment-audit.html\" target=\"_blank\" rel=\"noopener noreferrer\">MySQL 5.7 Installing the MySQL Enterprise Audit Plugin<\/a><\/li>\n<li><a href=\"https:\/\/dev.mysql.com\/doc\/refman\/5.7\/en\/audit-log.html\" target=\"_blank\" rel=\"noopener noreferrer\">MySQL 5.7\u00a0Enterprise Audit<\/a><\/li>\n<li><a href=\"https:\/\/www.mysql.com\/products\/enterprise\/audit.html\" target=\"_blank\" rel=\"noopener noreferrer\">Presentation of MySQL 5.7 Enterprise Audit<\/a><\/li>\n<\/ul>\n<h3>MySQL Security<\/h3>\n<ul>\n<li><a href=\"https:\/\/dev.mysql.com\/doc\/mysql-security-excerpt\/5.7\/en\/\" target=\"_blank\" rel=\"noopener noreferrer\">Security in MySQL<\/a><\/li>\n<li><a href=\"https:\/\/dev.mysql.com\/doc\/mysql-secure-deployment-guide\/5.7\/en\/\" target=\"_blank\" rel=\"noopener noreferrer\">MySQL 5.7 Secure Deployment Guide<\/a><\/li>\n<li><a href=\"https:\/\/dev.mysql.com\/doc\/refman\/5.7\/en\/security.html\" target=\"_blank\" rel=\"noopener noreferrer\">MySQL 5.7 Reference Manual &#8211; Security<\/a><\/li>\n<\/ul>\n\n\n\n\n<div class=\"wp-block-group\"><div class=\"wp-block-group__inner-container is-layout-flow wp-block-group-is-layout-flow\">\n<p><strong><span style=\"text-decoration: underline;\">MySQL Security Serie &#8211; Password Management<\/span><\/strong><\/p>\n\n\n\n<ul class=\"wp-block-list\"><li><a aria-label=\"Random Password Generation? (opens in a new tab)\" href=\"http:\/\/dasini.net\/blog\/2020\/04\/15\/mysql-security-random-password-generation\/\" target=\"_blank\" rel=\"noreferrer noopener\" class=\"aioseop-link\">Random Password Generation<\/a><\/li><li><a aria-label=\"Password Expiration Policy? (opens in a new tab)\" href=\"http:\/\/dasini.net\/blog\/2020\/04\/21\/mysql-security-password-expiration-policy\/\" target=\"_blank\" rel=\"noreferrer noopener\" class=\"aioseop-link\">Password Expiration Policy<\/a><\/li><li><a aria-label=\"Password Reuse Policy? (opens in a new tab)\" href=\"http:\/\/dasini.net\/blog\/2020\/04\/28\/mysql-security-password-reuse-policy\/\" target=\"_blank\" rel=\"noreferrer noopener\" class=\"aioseop-link\">Password Reuse Policy<\/a><\/li><li><a aria-label=\"Password Verification-Required Policy? (opens in a new tab)\" href=\"http:\/\/dasini.net\/blog\/2020\/05\/05\/mysql-security-password-verification-required-policy\/\" target=\"_blank\" rel=\"noreferrer noopener\" class=\"aioseop-link\">Password Verification-Required Policy<\/a><\/li><li><a aria-label=\"Failed-Login Tracking and Temporary Account Locking? (opens in a new tab)\" href=\"http:\/\/dasini.net\/blog\/2020\/05\/12\/mysql-security-failed-login-tracking-and-temporary-account-locking\/\" target=\"_blank\" rel=\"noreferrer noopener\" class=\"aioseop-link\">Failed-Login Tracking and Temporary Account Locking<\/a><\/li><li><a href=\"http:\/\/dasini.net\/blog\/2020\/05\/19\/mysql-security-dual-password-support\/\" target=\"_blank\" aria-label=\"Dual Password Support (opens in a new tab)\" rel=\"noreferrer noopener\" class=\"aioseop-link\">Dual Password Support<\/a><\/li><\/ul>\n\n\n<\/div><\/div>\n\n\n\n<div class=\"wp-block-group\"><div class=\"wp-block-group__inner-container is-layout-flow wp-block-group-is-layout-flow\">\n<p><strong><a rel=\"noreferrer noopener\" href=\"https:\/\/www.youtube.com\/channel\/UC12TulyJsJZHoCmby3Nm3WQ\" target=\"_blank\" class=\"aioseop-link\"><span style=\"text-decoration: underline;\">Olivier&rsquo;s MySQL Channel<\/span><\/a><\/strong><\/p>\n\n\n\n<ul class=\"wp-block-list\"><li><a href=\"https:\/\/youtu.be\/k4K-scd4oI0\" target=\"_blank\" rel=\"noreferrer noopener\" title=\"Establish a policy for password expiration with MySQL\">Establish a policy for password expiration with MySQL<\/a><\/li><li><a href=\"https:\/\/youtu.be\/VND0KvuX7bc\" target=\"_blank\" rel=\"noreferrer noopener\" title=\"Create users with a random password in MySQL\">Create users with a random password in MySQL<\/a><\/li><li><a href=\"https:\/\/www.youtube.com\/watch?v=4z6-dlGT-Mc\" target=\"_blank\" rel=\"noreferrer noopener\" title=\"Enable restrictions on reuse of previous passwords with MySQL\">Enable restrictions on reuse of previous passwords with MySQL<\/a><\/li><li><a href=\"https:\/\/www.youtube.com\/watch?v=1RwlU14TDWk\" target=\"_blank\" rel=\"noreferrer noopener\" title=\"Require MySQL users to provide their current password to change it\">Require MySQL users to provide their current password to change it<\/a><\/li><li><a href=\"https:\/\/www.youtube.com\/watch?v=6HO_ciRbiXw\" target=\"_blank\" rel=\"noreferrer noopener\" title=\"Temporary Account Locking in MySQL\">Temporary Account Locking in MySQL<\/a><\/li><\/ul>\n\n\n<\/div><\/div>\n\n\n\n<div style=\"height:20px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<p><a href=\"https:\/\/www.linkedin.com\/groups\/12524512\/\" target=\"_blank\" rel=\"noopener\" title=\"Olivier DASINI on Linkedin\">Follow me on Linkedin<\/a><\/p>\n\n\n\n<p>Watch my videos on my <a href=\"https:\/\/www.youtube.com\/channel\/UC12TulyJsJZHoCmby3Nm3WQ\" target=\"_blank\" rel=\"noreferrer noopener\" title=\"Olivier's MySQL Channel\">YouTube channel<\/a> and <a href=\"https:\/\/www.youtube.com\/channel\/UC12TulyJsJZHoCmby3Nm3WQ\/?sub_confirmation=1\" target=\"_blank\" rel=\"noreferrer noopener\" title=\"Subscribe\">subscribe<\/a>.<\/p>\n\n\n\n<p>My <a href=\"https:\/\/www.slideshare.net\/freshdaz\" target=\"_blank\" rel=\"noreferrer noopener\" title=\"Olivier DASINI on Slideshare\">Slideshare account<\/a>.<\/p>\n\n\n\n<p>My <a href=\"https:\/\/speakerdeck.com\/freshdaz\/\" target=\"_blank\" rel=\"noreferrer noopener\" title=\"Olivier DASINI on Speaker Deck\">Speaker Deck account<\/a>.<\/p>\n\n\n\n<div style=\"height:25px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<p class=\"has-vivid-red-color has-text-color\"><strong>Thanks for using HeatWave &amp; MySQL!<\/strong><\/p>\n","protected":false},"excerpt":{"rendered":"<p>In order to spot database misuse and\/or to prove compliance to popular regulations including GDPR, PCI DSS, HIPAA, &#8230; database administrators can be required to record and audit database activities. In this fifth episode of the\u00a0MySQL Security series, we will see what MySQL Enterprise Audit provide to help organizations implement stronger security controls and satisfy regulatory compliance.<\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"jetpack_post_was_ever_published":false,"footnotes":""},"categories":[203,365,339],"tags":[385,383,367],"class_list":["post-2222","post","type-post","status-publish","format-standard","hentry","category-mysql-en","category-security","category-tuto-en","tag-audit-en","tag-mysql-enterprise","tag-security"],"aioseo_notices":[],"jetpack_featured_media_url":"","jetpack_sharing_enabled":true,"jetpack_shortlink":"https:\/\/wp.me\/p9LfWW-zQ","jetpack-related-posts":[{"id":2177,"url":"https:\/\/dasini.net\/blog\/2018\/03\/14\/mysql-security-user-account-locking\/","url_meta":{"origin":2222,"position":0},"title":"MySQL Security &#8211; User Account Locking","author":"Olivier DASINI","date":"14 mars 2018","format":false,"excerpt":"For security reasons some context require you to setup a user account locking policy. Thus an unauthorized user is not able (anymore) to login to the MySQL server. In this 3rd article of the\u00a0MySQL 5.7 Security series, we\u00a0will see how to [un]lock a user account.","rel":"","context":"Dans &quot;MySQL&quot;","block_context":{"text":"MySQL","link":"https:\/\/dasini.net\/blog\/category\/mysql-en\/"},"img":{"alt_text":"MySQL Security","src":"https:\/\/i0.wp.com\/dasini.net\/blog\/wp-content\/uploads\/MySQL_DB_Lock2.png?resize=350%2C200","width":350,"height":200},"classes":[]},{"id":3557,"url":"https:\/\/dasini.net\/blog\/2020\/04\/15\/mysql-security-random-password-generation\/","url_meta":{"origin":2222,"position":1},"title":"MySQL Security &#8211; Random Password Generation","author":"Olivier DASINI","date":"15 avril 2020","format":false,"excerpt":"MySQL has the capability of generating random passwords for user accounts, as an alternative to requiring explicit administrator-specified literal passwords.","rel":"","context":"Dans &quot;MySQL&quot;","block_context":{"text":"MySQL","link":"https:\/\/dasini.net\/blog\/category\/mysql-en\/"},"img":{"alt_text":"","src":"https:\/\/i0.wp.com\/img.youtube.com\/vi\/VND0KvuX7bc\/0.jpg?resize=350%2C200","width":350,"height":200},"classes":[]},{"id":7929,"url":"https:\/\/dasini.net\/blog\/2025\/07\/03\/heatwave-mysql-database-audit\/","url_meta":{"origin":2222,"position":2},"title":"HeatWave MySQL Database Audit","author":"Olivier DASINI","date":"3 juillet 2025","format":false,"excerpt":"HeatWave MySQL Database Audit brings powerful enterprise-grade auditing capabilities to the cloud, allowing organizations to monitor and track database activity for security, compliance, and performance optimization. With features like customizable filters, real-time monitoring, minimal overhead, and seamless integration with MySQL tools, it enables administrators to log critical operations, detect threats,\u2026","rel":"","context":"Dans &quot;HeatWave&quot;","block_context":{"text":"HeatWave","link":"https:\/\/dasini.net\/blog\/category\/heatwave-en\/"},"img":{"alt_text":"HeatWave","src":"https:\/\/i0.wp.com\/dasini.net\/blog\/wp-content\/uploads\/2024\/12\/HeatWave_logo.png?resize=350%2C200&ssl=1","width":350,"height":200,"srcset":"https:\/\/i0.wp.com\/dasini.net\/blog\/wp-content\/uploads\/2024\/12\/HeatWave_logo.png?resize=350%2C200&ssl=1 1x, https:\/\/i0.wp.com\/dasini.net\/blog\/wp-content\/uploads\/2024\/12\/HeatWave_logo.png?resize=525%2C300&ssl=1 1.5x, https:\/\/i0.wp.com\/dasini.net\/blog\/wp-content\/uploads\/2024\/12\/HeatWave_logo.png?resize=700%2C400&ssl=1 2x"},"classes":[]},{"id":2248,"url":"https:\/\/dasini.net\/blog\/2018\/04\/10\/mysql-security-mysql-enterprise-transparent-data-encryption\/","url_meta":{"origin":2222,"position":3},"title":"MySQL Security &#8211; MySQL Enterprise Transparent Data Encryption","author":"Olivier DASINI","date":"10 avril 2018","format":false,"excerpt":"In this sixth episode of the\u00a0MySQL Security series, we will see\u00a0how data-at-rest encryption\u00a0helps organizations implement stronger security controls and satisfy regulatory compliance. You will be able to protect the privacy of your information, prevent data breaches and help meet popular regulatory requirements including GDPR, PCI DSS, HIPAA with MySQL Enterprise\u2026","rel":"","context":"Dans &quot;MySQL&quot;","block_context":{"text":"MySQL","link":"https:\/\/dasini.net\/blog\/category\/mysql-en\/"},"img":{"alt_text":"MySQL Security","src":"https:\/\/i0.wp.com\/dasini.net\/blog\/wp-content\/uploads\/MySQL_DB_Lock2.png?resize=350%2C200","width":350,"height":200},"classes":[]},{"id":2778,"url":"https:\/\/dasini.net\/blog\/2019\/03\/19\/mysql-security-mysql-enterprise-data-masking-and-de-identification\/","url_meta":{"origin":2222,"position":4},"title":"MySQL Security &#8211; MySQL Enterprise Data Masking and De-Identification","author":"Olivier DASINI","date":"19 mars 2019","format":false,"excerpt":"MySQL Enterprise Data Masking and De-Identification hides sensitive information by replacing real values with substitutes in order to protect sensitive data while they are still look real and consistent.","rel":"","context":"Dans &quot;MySQL&quot;","block_context":{"text":"MySQL","link":"https:\/\/dasini.net\/blog\/category\/mysql-en\/"},"img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":2156,"url":"https:\/\/dasini.net\/blog\/2018\/03\/07\/mysql-security-password-management\/","url_meta":{"origin":2222,"position":5},"title":"MySQL Security \u2013 Password Management","author":"Olivier DASINI","date":"7 mars 2018","format":false,"excerpt":"Some regulations required\u00a0that the password is renewed in a timely and appropriate manner (e.g. every 90 days). In this article, 2nd of the MySQL 5.7 Security series, we will see how to\u00a0to establish a policy for password expiration\u00a0with MySQL 5.7\u00a0Password\u00a0Management.","rel":"","context":"Dans &quot;MySQL&quot;","block_context":{"text":"MySQL","link":"https:\/\/dasini.net\/blog\/category\/mysql-en\/"},"img":{"alt_text":"MySQL Security","src":"https:\/\/i0.wp.com\/dasini.net\/blog\/wp-content\/uploads\/MySQL_DB_Lock2.png?resize=350%2C200","width":350,"height":200},"classes":[]}],"jetpack_likes_enabled":true,"_links":{"self":[{"href":"https:\/\/dasini.net\/blog\/wp-json\/wp\/v2\/posts\/2222","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/dasini.net\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/dasini.net\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/dasini.net\/blog\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/dasini.net\/blog\/wp-json\/wp\/v2\/comments?post=2222"}],"version-history":[{"count":50,"href":"https:\/\/dasini.net\/blog\/wp-json\/wp\/v2\/posts\/2222\/revisions"}],"predecessor-version":[{"id":7937,"href":"https:\/\/dasini.net\/blog\/wp-json\/wp\/v2\/posts\/2222\/revisions\/7937"}],"wp:attachment":[{"href":"https:\/\/dasini.net\/blog\/wp-json\/wp\/v2\/media?parent=2222"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/dasini.net\/blog\/wp-json\/wp\/v2\/categories?post=2222"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/dasini.net\/blog\/wp-json\/wp\/v2\/tags?post=2222"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}