
{"id":2353,"date":"2018-04-16T11:36:09","date_gmt":"2018-04-16T10:36:09","guid":{"rendered":"http:\/\/dasini.net\/blog\/?p=2353"},"modified":"2020-05-27T07:50:21","modified_gmt":"2020-05-27T06:50:21","slug":"mysql-security-mysql-enterprise-firewall","status":"publish","type":"post","link":"https:\/\/dasini.net\/blog\/2018\/04\/16\/mysql-security-mysql-enterprise-firewall\/","title":{"rendered":"MySQL Security \u2013 MySQL Enterprise Firewall"},"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\u00a0<a href=\"http:\/\/dasini.net\/blog\/2018\/04\/04\/mysql-security-mysql-enterprise-audit\/\" target=\"_blank\" rel=\"noopener noreferrer\">Audit<\/a>,\u00a0<a href=\"http:\/\/dasini.net\/blog\/2018\/04\/10\/mysql-security-mysql-enterprise-transparent-data-encryption\/\" target=\"_blank\" rel=\"noopener noreferrer\">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\/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>, <a href=\"http:\/\/dasini.net\/blog\/2018\/03\/14\/mysql-security-user-account-locking\/\" target=\"_blank\" rel=\"noopener noreferrer\">User Account Locking<\/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 this seventh episode of the\u00a0<strong>MySQL Security<\/strong> series, we will see how <strong><a href=\"https:\/\/www.mysql.com\/products\/enterprise\/firewall.html\" target=\"_blank\" rel=\"noopener noreferrer\">MySQL Enterprise Firewall<\/a><\/strong> can help you to strengthen the protection of your data, in <strong>real-time<\/strong>, against cyber security threats including\u00a0<strong>SQL Injection attacks<\/strong> by monitoring, alerting, and blocking unauthorized database activity <strong>without any changes<\/strong> to your applications.<\/p>\n<h2>Installing the MySQL Enterprise Firewall Plugin<\/h2>\n<p>MySQL Enterprise Firewall installation is an easy one-time operation that involves running a script (e.g. <strong><em>linux_install_firewall.sql<\/em><\/strong> in this blog post (Linux and similar systems that use <strong>.so<\/strong> as the file name suffix); <strong>win_install_firewall.sql<\/strong> for Windows systems that use <strong>.dll<\/strong>\u00a0as the file name suffix) located in the <em><strong>share<\/strong><\/em> directory of your MySQL installation.<\/p>\n<p>I&rsquo;m using MySQL <strong>5.7.21 Enterprise Edition<\/strong> :<\/p>\n<pre class=\"lang:mysql decode:true\" title=\"MySQL 5.7.21 Enterprise Edition\">mysql&gt; \nSELECT 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 Firewall\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<\/strong> <strong>8.0.12<\/strong>+<\/em><\/p>\n<p>MySQL Enterprise Firewall does not work together with the <a href=\"https:\/\/dev.mysql.com\/doc\/refman\/5.7\/en\/query-cache.html\" target=\"_blank\" rel=\"noopener noreferrer\">MySQL Query Cache<\/a>. Fortunately the query cache is disabled by default.<\/p>\n<pre class=\"lang:mysql decode:true\" title=\"Query cache must be disabled\">mysql&gt; \nSHOW VARIABLES LIKE 'query_cache_type';\n+------------------+-------+\n| Variable_name    | Value |\n+------------------+-------+\n| query_cache_type | OFF   |\n+------------------+-------+<\/pre>\n<p><em><span style=\"text-decoration: underline;\">Note<\/span><\/em><br \/><em>The query cache is deprecated as of MySQL 5.7.20, and is <a href=\"https:\/\/mysqlserverteam.com\/mysql-8-0-retiring-support-for-the-query-cache\/\" target=\"_blank\" rel=\"noopener noreferrer\">removed in MySQL 8.0<\/a>.<\/em><\/p>\n<p><em><span style=\"text-decoration: underline;\">Note<\/span><\/em><br \/><em>For a great query cache tuning advice from <a href=\"https:\/\/dom.as\/domas\/\" target=\"_blank\" rel=\"noopener noreferrer\">Domas Mituzas<\/a>\u00a0: <a href=\"https:\/\/dom.as\/2009\/07\/08\/query-cache-tuning\/\" target=\"_blank\" rel=\"noopener noreferrer\">click here<\/a> \ud83d\ude42<\/em><\/p>\n<p>Now we can installed the Firewall<\/p>\n<pre class=\"lang:mysql decode:true\" title=\"Installing MySQL Enterprise Firewall\">mysql&gt; \nSOURCE \/usr\/share\/mysql\/linux_install_firewall.sql\n\nDatabase changed\nQuery OK, 0 rows affected (0.07 sec)\n\nQuery OK, 0 rows affected (0.06 sec)\n\nQuery OK, 0 rows affected (0.01 sec)\n\nQuery OK, 0 rows affected (0.00 sec)\n\nQuery OK, 0 rows affected (0.01 sec)\n\nQuery OK, 0 rows affected (0.00 sec)\n\nQuery OK, 0 rows affected (0.00 sec)\n\nQuery OK, 0 rows affected (0.01 sec)\n\nQuery OK, 0 rows affected (0.01 sec)\n\nQuery OK, 0 rows affected (0.01 sec)\n\nQuery OK, 0 rows affected (0.01 sec)\n\nQuery OK, 0 rows affected (0.01 sec)<\/pre>\n<p>And check if it has been launched with the system variable\u00a0<em><strong><a href=\"https:\/\/dev.mysql.com\/doc\/refman\/5.7\/en\/firewall-reference.html#sysvar_mysql_firewall_mode\" target=\"_blank\" rel=\"noopener noreferrer\">mysql_firewall_mode<\/a><\/strong><\/em>\u00a0:<\/p>\n<pre class=\"lang:mysql decode:true\" title=\"Firewall enabled\">mysql&gt; \nSHOW GLOBAL VARIABLES LIKE 'mysql_firewall_mode';\n+---------------------+-------+\n| Variable_name       | Value |\n+---------------------+-------+\n| mysql_firewall_mode | ON    |\n+---------------------+-------+<\/pre>\n<p>Alternatively, we can also add <strong><em>mysql_firewall_mode<\/em><\/strong> under the<strong><em> [mysqld]<\/em> <\/strong>option group in the MySQL configuration file :<\/p>\n<pre class=\"lang:vim decode:true\" title=\"Enabling the firewall in the config file\">[mysqld]\nmysql_firewall_mode=ON<\/pre>\n<p>It is also possible to disable or enable the firewall at runtime :<\/p>\n<pre class=\"lang:mysql decode:true\" title=\"Disable or enable the firewall at runtime\">mysql&gt; \nSET GLOBAL mysql_firewall_mode = OFF;\n\nSET GLOBAL mysql_firewall_mode = ON;<\/pre>\n<h2>Playtime<\/h2>\n<p>The MySQL Firewall is installed!<\/p>\n<p>Let&rsquo;s assume now we have an application that uses the schema <em><a href=\"https:\/\/dev.mysql.com\/doc\/sakila\/en\/\" target=\"_blank\" rel=\"noopener noreferrer\">sakila<\/a><\/em> in this instance. This application has a dedicated user account (myApp@localhost) and all the privileges on <em>sakila<\/em> :<\/p>\n<pre class=\"lang:mysql decode:true\" title=\"Application privileges\">mysql&gt; \nCREATE USER 'myApp'@'localhost' IDENTIFIED BY 'AppU$3rPwd';\n\nGRANT ALL ON sakila.* TO 'myApp'@'localhost';<\/pre>\n<p><strong><span style=\"text-decoration: underline;\"><em>Note<\/em><\/span><\/strong><\/p>\n<p><em>The firewall maintains whitelist rules on a per-account basis.<\/em><\/p>\n<p>Regular queries from this\u00a0hypothetical application are :<\/p>\n<ul>\n<li>UPDATE rental SET return_date = &lt;date&gt; WHERE rental_id = &lt;id&gt;;<\/li>\n<li>SELECT get_customer_balance(&lt;id&gt;, &lt;date&gt;);<\/li>\n<\/ul>\n<p>But first, users are authenticated with :<\/p>\n<ul>\n<li>SELECT staff_id, first_name, email, last_name, username, password FROM staff WHERE username = &lsquo;&lt;userName&gt;&rsquo; AND password=sha1(&lt;userPassword&gt;);<\/li>\n<\/ul>\n<p><em><strong><span style=\"text-decoration: underline;\">Warning<\/span><\/strong><\/em><br \/><em>Query above is not safe nor optimal for production.<\/em><\/p>\n<p>Firewall allows 3 modes :<\/p>\n<ul>\n<li><span style=\"text-decoration: underline;\"><strong>recording<\/strong><\/span>, the firewall adds the normalized statement to the account whitelist rules.<\/li>\n<li><span style=\"text-decoration: underline;\"><strong>protecting<\/strong><\/span>, 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. The firewall also writes the rejected statement to the error log if the <a href=\"https:\/\/dev.mysql.com\/doc\/refman\/5.7\/en\/firewall-reference.html#sysvar_mysql_firewall_trace\" target=\"_blank\" rel=\"noopener noreferrer\"><strong><em>mysql_firewall_trace<\/em><\/strong><\/a> system variable is enabled.<\/li>\n<li><span style=\"text-decoration: underline;\"><strong>detecting<\/strong><\/span>, the firewall matches statements as in protecting mode, but writes nonmatching statements to the error log without denying access.<\/li>\n<\/ul>\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_firewall.png?resize=763%2C160\" alt=\"MySQL Enterprise Firewall\" width=\"763\" height=\"160\" \/><\/p>\n<h3>Recording mode<\/h3>\n<p>Ok now we know our queries, let&rsquo;s go back to the Firewall.<\/p>\n<p>The basic and powerful idea of the MySQL Firewall is\u00a0to deny SQL statement execution based on matching against a whitelist. In other words the Firewall learns\u00a0acceptable statement patterns.<\/p>\n<p>In order to create this whitelist, we&rsquo;ll switch the Firewall in the <strong><em>RECORDING<\/em><\/strong> mode using\u00a0<strong><em>sp_set_firewall_mode<\/em><\/strong> <a href=\"https:\/\/dev.mysql.com\/doc\/refman\/5.7\/en\/firewall-reference.html#firewall-routines\" target=\"_blank\" rel=\"noopener noreferrer\">stored procedure<\/a> :<\/p>\n<pre class=\"lang:mysql decode:true\" title=\"register the account with the firewall and place it in recording mode\">mysql&gt; \n-- register the account with the firewall and place it in recording mode\nCALL mysql.sp_set_firewall_mode('myApp@localhost', 'RECORDING');\n\n+-----------------------------------------------+\n| read_firewall_whitelist(arg_userhost,FW.rule) |\n+-----------------------------------------------+\n| Imported users: 0\nImported rules: 0\n          |\n+-----------------------------------------------+\n1 row in set (0.00 sec)\n\nQuery OK, 0 rows affected (0.00 sec)<\/pre>\n<p>We can know see what is the status of the Firewall for any user with\u00a0<strong><em>INFORMATION_SCHEMA.MYSQL_FIREWALL_USERS<\/em><\/strong> table :<\/p>\n<pre class=\"lang:mysql decode:true\" title=\"Check firewall status\">mysql&gt; \n-- Check firewall status\nSELECT MODE \nFROM INFORMATION_SCHEMA.MYSQL_FIREWALL_USERS \nWHERE USERHOST = 'myApp@localhost';\n+-----------+\n| MODE      |\n+-----------+\n| RECORDING |\n+-----------+<\/pre>\n<p>During the recording mode, we can run the application. The queries generated by the application will be recorded in the Firewall&rsquo;s whitelist :<\/p>\n<pre class=\"lang:mysql decode:true\" title=\"Authentication query\"># Login failed (Mike \/ 0000)\nmysql_myApp&gt; \nSELECT staff_id, first_name, email, last_name, username, password \nFROM sakila.staff \nWHERE username = 'Mike' AND password=sha1(0000)\\G\nEmpty set (0.00 sec)\n\n\n# Login succeed (Mike \/ 12345)\nSELECT staff_id, first_name, email, last_name, username, password \nFROM sakila.staff \nWHERE username = 'Mike' AND password=sha1(12345)\\G\n*************************** 1. row ***************************\n  staff_id: 1\nfirst_name: Mike\n     email: Mike.Hillyer@sakilastaff.com\n last_name: Hillyer\n  username: Mike\n  password: 8cb2237d0679ca88db6464eac60da96345513964<\/pre>\n<p>Other queries&#8230;<\/p>\n<pre class=\"lang:mysql decode:true\" title=\"Regular queries\">mysql_myApp&gt;\nUPDATE rental SET return_date = NOW() WHERE rental_id = 1;\nQuery OK, 1 row affected (0.00 sec)\nRows matched: 1  Changed: 1  Warnings: 0\n\nSELECT get_customer_balance(1, NOW());\n+--------------------------------+\n| get_customer_balance(1, NOW()) |\n+--------------------------------+\n|                           0.00 |\n+--------------------------------+<\/pre>\n<p>And so on&#8230;<\/p>\n<p>When the training is done switch the Firewall to protecting mode.<\/p>\n<h3>Protecting mode<\/h3>\n<p>Use the <strong><em>sp_set_firewall_mode<\/em><\/strong> stored procedure to switch the registered user to protecting mode:<\/p>\n<pre class=\"lang:mysql decode:true \" title=\"Firewall in protecting mode\">mysql&gt; \n-- Switch the Firewall in protecting mode\nCALL mysql.sp_set_firewall_mode('myApp@localhost', 'PROTECTING');\nQuery OK, 3 rows affected (0.00 sec)\n\n\n-- Check\nSELECT * FROM INFORMATION_SCHEMA.MYSQL_FIREWALL_USERS;\n+------------------+------------+\n| USERHOST         | MODE       |\n+------------------+------------+\n| myApp@localhost  | PROTECTING |\n+------------------+------------+<\/pre>\n<p>Firewall stores SQL statements on a normalized digest form.\u00a0You can check the whitelist with\u00a0<em><strong>INFORMATION_SCHEMA.MYSQL_FIREWALL_WHITELIST<\/strong><\/em> table :<\/p>\n<pre class=\"lang:mysql decode:true\" title=\"Check whitelist content\">mysql&gt;\n-- Check whitelist content \nSELECT USERHOST, RULE \nFROM INFORMATION_SCHEMA.MYSQL_FIREWALL_WHITELIST \nWHERE USERHOST = 'myApp@localhost';\n\n+-----------------+----------------------------------------------------------------------------------------------------------------------------------------------------------+\n| USERHOST        | RULE                                                                                                                                                     |\n+-----------------+----------------------------------------------------------------------------------------------------------------------------------------------------------+\n| myApp@localhost | SELECT `get_customer_balance` ( ? , NOW ( ) )                                                                                                            |\n| myApp@localhost | UPDATE `rental` SET `return_date` = NOW ( ) WHERE `rental_id` = ?                                                                                        |\n| myApp@localhost | SELECT `staff_id` , `first_name` , `email` , `last_name` , `username` , PASSWORD FROM `sakila` . `staff` WHERE `username` = ? AND PASSWORD = `sha1` (?)  |\n+-----------------+----------------------------------------------------------------------------------------------------------------------------------------------------------+\n3 rows in set (0.00 sec)<\/pre>\n<p><strong><em><span style=\"text-decoration: underline;\">Note<\/span><\/em><\/strong><br \/><em>For additional training you can switch\u00a0back\u00a0recording mode or even update (<\/em>that is an UPDATE query<em>) this table if necessary using the <a href=\"https:\/\/dev.mysql.com\/doc\/refman\/5.7\/en\/firewall-reference.html#firewall-routines\" target=\"_blank\" rel=\"noopener noreferrer\">normalize_statement<\/a> UDF.<\/em><\/p>\n<p>In protecting mode, there are 2 kind of queries for the application point of view :<\/p>\n<ul>\n<li><span style=\"text-decoration: underline;\">Acceptable<\/span><\/li>\n<\/ul>\n<pre class=\"lang:mysql decode:true\" title=\"Acceptable and unacceptable statements\">mysql_app&gt; \nUPDATE rental SET return_date = NOW() WHERE rental_id = 1;\nQuery OK, 1 row affected (0.03 sec)\nRows matched: 1  Changed: 1  Warnings: 0\n\n\nUPDATE rental SET return_date = NOW() WHERE rental_id = 42;\nQuery OK, 1 row affected (0.03 sec)\nRows matched: 1  Changed: 1  Warnings: 0\n\n\nSELECT get_customer_balance(5, NOW());\n+--------------------------------+\n| get_customer_balance(5, NOW()) |\n+--------------------------------+\n|                           0.00 |\n+--------------------------------+\n1 row in set (0.01 sec)<\/pre>\n<ul>\n<li><span style=\"text-decoration: underline;\">Unacceptable<\/span><\/li>\n<\/ul>\n<pre class=\"lang:mysql decode:true \" title=\"Unacceptable statements\">mysql_app&gt;\nDROP TABLE rental;\nERROR 1045 (28000): Statement was blocked by Firewall\n\n\nUPDATE rental SET return_date = NOW();\nERROR 1045 (28000): Statement was blocked by Firewall<\/pre>\n<p><span style=\"text-decoration: underline;\"><strong>SQL injection<\/strong><\/span><\/p>\n<p>One of the big advantage of the MySQL Firewall is that it can help protect against\u00a0<a href=\"https:\/\/en.wikipedia.org\/wiki\/SQL_injection\" target=\"_blank\" rel=\"noopener noreferrer\">SQL Injection<\/a> attacks. In this post, I will not go into details of what is an SQL injection. However below a simplistic example to illustrate the\u00a0overall principle.<\/p>\n<p>User name and password are needed for authentication :<\/p>\n<pre class=\"lang:mysql decode:true\" title=\"Login OK\">mysql_app&gt; \n-- user = Mike : OK\n-- password = 12345 : OK\n-- Login OK\n\nSELECT staff_id, first_name, email, last_name, username, password \nFROM sakila.staff \nWHERE username = 'Mike' AND password=sha1(12345)\\G\n*************************** 1. row ***************************\n  staff_id: 1\nfirst_name: Mike\n     email: Mike.Hillyer@sakilastaff.com\n last_name: Hillyer\n  username: Mike\n  password: 8cb2237d0679ca88db6464eac60da96345513964\n<\/pre>\n<p>Low quality code can generate unsafe queries :<\/p>\n<pre class=\"lang:mysql decode:true\" title=\"Unsafe query\">mysql&gt;\n-- user = Mike' AND '1'='1'\\G --  : Not OK\n-- password = &lt;whatever&gt;\n-- SQL injection \/!\\\n\nSELECT staff_id, first_name, email, last_name, username, password \nFROM sakila.staff \nWHERE username = 'Mike' AND '1'='1'\\G --   AND password=sha1(&lt;whatever&gt;)\\G\n*************************** 1. row ***************************\n  staff_id: 1\nfirst_name: Mike\n     email: Mike.Hillyer@sakilastaff.com\n last_name: Hillyer\n  username: Mike\n  password: 8cb2237d0679ca88db6464eac60da96345513964<\/pre>\n<p>Fortunately they are blocked by the MySQL Firewall :<\/p>\n<pre class=\"lang:mysql decode:true\" title=\"Unsafe query blocked by Firewall\">mysql_app&gt;\n-- user = Mike' AND '1'='1'\\G --  : Not OK\n-- password = &lt;whatever&gt;\n-- Blocked by Firewall because not matching whitelist\n\nSELECT staff_id, first_name, email, last_name, username, password \nFROM sakila.staff \nWHERE username = 'Mike' AND '1'='1'\\G --   AND password=sha1(12345)\\G\n\nERROR 1045 (28000): Statement was blocked by Firewall\n\n<\/pre>\n<p>Rejected queries can be seen in the MySQL <a href=\"https:\/\/dev.mysql.com\/doc\/refman\/5.7\/en\/glossary.html#glos_error_log\" target=\"_blank\" rel=\"noopener noreferrer\">error log<\/a>\u00a0if the <a href=\"https:\/\/dev.mysql.com\/doc\/refman\/5.7\/en\/firewall-reference.html#sysvar_mysql_firewall_trace\" target=\"_blank\" rel=\"noopener noreferrer\"><strong><em>mysql_firewall_trace<\/em><\/strong><\/a> system variable is enabled.<\/p>\n<pre class=\"lang:mysql decode:true\" title=\"See rejected statements\">mysql&gt; \nSET GLOBAL mysql_firewall_trace = ON;\nQuery OK, 0 rows affected (0.00 sec)\n\n\nSHOW VARIABLES LIKE 'mysql_firewall_trace';\n+----------------------+-------+\n| Variable_name        | Value |\n+----------------------+-------+\n| mysql_firewall_trace | ON    |\n+----------------------+-------+<\/pre>\n<p>Unacceptable queries :<\/p>\n<pre class=\"lang:mysql decode:true\" title=\" Statements blocked by Firewall\">mysql_app&gt; \nSELECT * FROM sakila.staff;\nERROR 1045 (28000): Statement was blocked by Firewall\n\nDELETE FROM rental;\nERROR 1045 (28000): Statement was blocked by Firewall\n\nTRUNCATE mysql.user;\nERROR 1045 (28000): Statement was blocked by Firewall<\/pre>\n<p>are available in the MySQL error log :<\/p>\n<pre class=\"lang:mysql decode:true\" title=\"Monitor rejected statements in the error log\">mysql&gt; \nsystem tail -n3 \/var\/log\/mysqld.log\n\n2018-04-16T08:46:09.353950Z 1966 [Note] Plugin MYSQL_FIREWALL reported: 'ACCESS DENIED for 'myApp@localhost'. Reason: No match in whitelist. Statement: SELECT * FROM `sakila` . `staff` '\n2018-04-16T08:46:09.354198Z 1966 [Note] Plugin MYSQL_FIREWALL reported: 'ACCESS DENIED for 'myApp@localhost'. Reason: No match in whitelist. Statement: DELETE FROM `rental` '\n2018-04-16T08:46:09.354403Z 1966 [Note] Plugin MYSQL_FIREWALL reported: 'ACCESS DENIED for 'myApp@localhost'. Reason: No match in whitelist. Statement: TRUNCATE `mysql` . `user`<\/pre>\n<h3>Detecting mode<\/h3>\n<p>MySQL Enterprise Firewall can also be used\u00a0into a <strong>intrusion-detecting mode<\/strong> that writes suspicious statements to the error log but <span style=\"text-decoration: underline;\">does not<\/span> deny access.<\/p>\n<pre class=\"lang:mysql decode:true\" title=\"Enable intrusion-detecting mode\">mysql&gt; \nCALL mysql.sp_set_firewall_mode('myApp@localhost', 'DETECTING');\nQuery OK, 3 rows affected (0.06 sec)\n\n\nSELECT * FROM INFORMATION_SCHEMA.MYSQL_FIREWALL_USERS;\n+-----------------+-----------+\n| USERHOST        | MODE      |\n+-----------------+-----------+\n| myApp@localhost | DETECTING |\n+-----------------+-----------+<\/pre>\n<p>Now using the application user account, suspicious queries will not be blocked :<\/p>\n<pre class=\"lang:mysql decode:true \" title=\"Suspicious query\">mysql_app&gt; \n-- Suspicious query\nSHOW TABLES LIKE 'customer%';\n+------------------------------+\n| Tables_in_sakila (customer%) |\n+------------------------------+\n| customer                     |\n| customer_list                |\n+------------------------------+\n2 rows in set (0.00 sec)<\/pre>\n<p>however a message is written into the MySQL error log :<\/p>\n<pre class=\"lang:mysql decode:true \" title=\"Suspicious queries are written in the error log\">mysql&gt; \nsystem tail -n1 \/var\/log\/mysqld.log\n2018-04-16T09:01:47.133398Z 1966 [Note] Plugin MYSQL_FIREWALL reported: 'SUSPICIOUS STATEMENT from 'myApp@localhost'. Reason: No match in whitelist. Statement: SHOW TABLES LIKE ? '<\/pre>\n<h2>Monitor the Firewall<\/h2>\n<p>MySQL Enterprise Firewall provides the following <a href=\"https:\/\/dev.mysql.com\/doc\/refman\/5.7\/en\/firewall-reference.html#firewall-status-variables\" target=\"_blank\" rel=\"noopener noreferrer\">status variables<\/a> :<\/p>\n<ul>\n<li><a href=\"https:\/\/dev.mysql.com\/doc\/refman\/5.7\/en\/firewall-reference.html#statvar_Firewall_access_denied\" target=\"_blank\" rel=\"noopener noreferrer\"><em><strong>Firewall_access_denied<\/strong><\/em><\/a> : The number of statements rejected by MySQL Enterprise Firewall.<\/li>\n<li><a href=\"https:\/\/dev.mysql.com\/doc\/refman\/5.7\/en\/firewall-reference.html#statvar_Firewall_access_granted\" target=\"_blank\" rel=\"noopener noreferrer\"><em><strong>Firewall_access_granted<\/strong><\/em><\/a> : The number of statements accepted by MySQL Enterprise Firewall.<\/li>\n<li><a href=\"https:\/\/dev.mysql.com\/doc\/refman\/5.7\/en\/firewall-reference.html#statvar_Firewall_access_suspicious\" target=\"_blank\" rel=\"noopener noreferrer\"><em><strong>Firewall_access_suspicious<\/strong><\/em><\/a> : The number of statements logged by MySQL Enterprise Firewall as suspicious for users who are in DETECTING mode.<\/li>\n<li><a href=\"https:\/\/dev.mysql.com\/doc\/refman\/5.7\/en\/firewall-reference.html#statvar_Firewall_cached_entries\" target=\"_blank\" rel=\"noopener noreferrer\"><em><strong>Firewall_cached_entries<\/strong><\/em><\/a> : The number of statements recorded by MySQL Enterprise Firewall, including duplicates.<\/li>\n<\/ul>\n<pre class=\"lang:mysql decode:true \" title=\"Monitoring the Firewall\">mysql&gt; \nSHOW GLOBAL STATUS LIKE 'Firewall%';\n+----------------------------+-------+\n| Variable_name              | Value |\n+----------------------------+-------+\n| Firewall_access_denied     | 9     |\n| Firewall_access_granted    | 8     |\n| Firewall_access_suspicious | 1     |\n| Firewall_cached_entries    | 3     |\n+----------------------------+-------+<\/pre>\n<h2>Uninstall the Firewall<\/h2>\n<p>To remove MySQL Enterprise Firewall, execute the following statements :<\/p>\n<pre class=\"lang:mysql decode:true\" title=\"Uninstall the firewall\">mysql&gt; \nUSE mysql;\n\nDROP TABLE mysql.firewall_whitelist;\nDROP TABLE mysql.firewall_users;\nUNINSTALL PLUGIN mysql_firewall;\nUNINSTALL PLUGIN mysql_firewall_whitelist;\nUNINSTALL PLUGIN mysql_firewall_users;\nDROP FUNCTION set_firewall_mode;\nDROP FUNCTION normalize_statement;\nDROP FUNCTION read_firewall_whitelist;\nDROP FUNCTION read_firewall_users;\nDROP FUNCTION mysql_firewall_flush_status;\nDROP PROCEDURE mysql.sp_set_firewall_mode;\nDROP PROCEDURE mysql.sp_reload_firewall_rules;<\/pre>\n<p><strong><span style=\"text-decoration: underline;\"><em>Note<\/em><\/span><\/strong><br \/><em>You may have to kill the application remaining connections (e.g. <a href=\"https:\/\/dev.mysql.com\/doc\/refman\/5.7\/en\/kill.html\" target=\"_blank\" rel=\"noopener noreferrer\">KILL CONNECTION<\/a>) or reconnect the application user (e.g. mysql&gt; <a href=\"https:\/\/dev.mysql.com\/doc\/refman\/5.7\/en\/mysql-commands.html\" target=\"_blank\" rel=\"noopener noreferrer\">connect<\/a>)<\/em><\/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<em>(Note &#8211; Select Product Pack: MySQL Database)<\/em>.<\/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:\/\/www.mysql.com\/products\/enterprise\/firewall.html\" target=\"_blank\" rel=\"noopener noreferrer\">MySQL Enterprise Firewall High lights<\/a><\/li>\n<li><a href=\"https:\/\/dev.mysql.com\/doc\/mysql-secure-deployment-guide\/5.7\/en\/secure-deployment-firewall.html\" target=\"_blank\" rel=\"noopener noreferrer\">Installing the MySQL Enterprise Firewall Plugin<\/a><\/li>\n<li><a href=\"https:\/\/dev.mysql.com\/doc\/mysql-security-excerpt\/5.7\/en\/firewall.html\" target=\"_blank\" rel=\"noopener noreferrer\">MySQL Enterprise Firewall<\/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:75px\" 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 MySQL!<\/strong><\/p>\n\n\n\n<div style=\"height:25px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/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 this seventh episode of the\u00a0MySQL Security series, we will see how MySQL Enterprise Firewall can help you to strengthen the protection of your data, in real-time, against cyber security threats like SQL Injection attacks by monitoring, alerting, and blocking unauthorized database activity without any changes to your applications.<\/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":[389,383,367],"class_list":["post-2353","post","type-post","status-publish","format-standard","hentry","category-mysql-en","category-security","category-tuto-en","tag-firewall","tag-mysql-enterprise","tag-security"],"aioseo_notices":[],"jetpack_featured_media_url":"","jetpack_sharing_enabled":true,"jetpack_shortlink":"https:\/\/wp.me\/p9LfWW-BX","jetpack-related-posts":[{"id":3557,"url":"https:\/\/dasini.net\/blog\/2020\/04\/15\/mysql-security-random-password-generation\/","url_meta":{"origin":2353,"position":0},"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":2177,"url":"https:\/\/dasini.net\/blog\/2018\/03\/14\/mysql-security-user-account-locking\/","url_meta":{"origin":2353,"position":1},"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":2136,"url":"https:\/\/dasini.net\/blog\/2018\/03\/01\/mysql-security-password-validation-plugin\/","url_meta":{"origin":2353,"position":2},"title":"MySQL Security \u2013 Password Validation Plugin","author":"Olivier DASINI","date":"1 mars 2018","format":false,"excerpt":"In this article, 1st of a MySQL 5.7 Security series, we will see how to enforce Strong Passwords with Password Validation Plugin when using MySQL 5.7. Authentication with ID and password is a very simple and common (because it\u2019s simple) way to secure the access to a resource, however the\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":2353,"position":3},"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":3567,"url":"https:\/\/dasini.net\/blog\/2020\/05\/19\/mysql-security-dual-password-support\/","url_meta":{"origin":2353,"position":4},"title":"MySQL Security &#8211; Dual Password Support","author":"Olivier DASINI","date":"19 mai 2020","format":false,"excerpt":"Dual-password capability makes it possible to seamlessly perform credential changes without downtime.","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":2248,"url":"https:\/\/dasini.net\/blog\/2018\/04\/10\/mysql-security-mysql-enterprise-transparent-data-encryption\/","url_meta":{"origin":2353,"position":5},"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":[]}],"jetpack_likes_enabled":true,"_links":{"self":[{"href":"https:\/\/dasini.net\/blog\/wp-json\/wp\/v2\/posts\/2353","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=2353"}],"version-history":[{"count":40,"href":"https:\/\/dasini.net\/blog\/wp-json\/wp\/v2\/posts\/2353\/revisions"}],"predecessor-version":[{"id":3733,"href":"https:\/\/dasini.net\/blog\/wp-json\/wp\/v2\/posts\/2353\/revisions\/3733"}],"wp:attachment":[{"href":"https:\/\/dasini.net\/blog\/wp-json\/wp\/v2\/media?parent=2353"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/dasini.net\/blog\/wp-json\/wp\/v2\/categories?post=2353"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/dasini.net\/blog\/wp-json\/wp\/v2\/tags?post=2353"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}