扩展 MySQL 9.0  /  ...  /  编写审计插件

4.4.8 编写审计插件

本节介绍如何编写服务器端审计插件,并使用 MySQL 源代码分发中 plugin/audit_null 目录中提供的示例插件。该目录中的 audit_null.caudit_null_variables.h 源文件实现了名为 NULL_AUDIT 的审计插件。

注意

使用审计插件 API 的其他插件示例包括查询重写插件(参见 重写器查询重写插件)和版本令牌插件(参见 版本令牌)。

在服务器内部,可插拔审计接口在 MySQL 源代码分发中 sql 目录下的 sql_audit.hsql_audit.cc 文件中实现。此外,服务器中的几个地方在发生可审计事件时会调用审计接口,以便在必要时通知注册的审计插件有关事件。要查看这些调用发生的位置,请搜索服务器源文件以查找调用名称类似于 mysql_audit_xxx() 的函数。以下服务器操作会发生审计通知

  • 客户端连接和断开连接事件

  • 将消息写入通用查询日志(如果日志已启用)

  • 将消息写入错误日志

  • 将查询结果发送到客户端

要编写审计插件,请在插件源文件中包含以下头文件。其他 MySQL 或通用头文件也可能需要,具体取决于插件的功能和要求。

#include <mysql/plugin_audit.h>

plugin_audit.h 包含 plugin.h,因此您不需要显式包含后者文件。plugin.h 定义了 MYSQL_AUDIT_PLUGIN 服务器插件类型以及声明插件所需的数据结构。plugin_audit.h 定义了特定于审计插件的数据结构。

审计插件通用描述符

审计插件,与任何 MySQL 服务器插件一样,都具有一个通用插件描述符(参见 第 4.4.2.1 节,“服务器插件库和插件描述符”)和一个特定于类型的插件描述符。audit_null.c 中,audit_null 的通用描述符如下所示

mysql_declare_plugin(audit_null)
{
  MYSQL_AUDIT_PLUGIN,         /* type                            */
  &audit_null_descriptor,     /* descriptor                      */
  "NULL_AUDIT",               /* name                            */
  "Oracle Corporation",       /* author                          */
  "Simple NULL Audit",        /* description                     */
  PLUGIN_LICENSE_GPL,
  audit_null_plugin_init,     /* init function (when loaded)     */
  audit_null_plugin_deinit,   /* deinit function (when unloaded) */
  0x0003,                     /* version                         */
  simple_status,              /* status variables                */
  system_variables,           /* system variables                */
  NULL,
  0,
}
mysql_declare_plugin_end;

第一个成员 MYSQL_AUDIT_PLUGIN 将此插件标识为审计插件。

audit_null_descriptor 指向特定于类型的插件描述符,将在后面介绍。

name 成员 (NULL_AUDIT) 指示在语句中引用插件时要使用的名称,例如 INSTALL PLUGINUNINSTALL PLUGIN。这也是 INFORMATION_SCHEMA.PLUGINSSHOW PLUGINS 显示的名称。

audit_null_plugin_init 初始化函数在加载插件时执行插件初始化。audit_null_plugin_deinit 函数在卸载插件时执行清理。

通用插件描述符还引用了 simple_statussystem_variables,这些结构公开了几个状态和系统变量。当插件启用时,可以使用 SHOW 语句(SHOW STATUSSHOW VARIABLES)或相应的性能模式表来检查这些变量。

simple_status 结构声明了几个状态变量,其名称格式为 Audit_null_xxxNULL_AUDIT 为其接收的每个通知都增加 Audit_null_called 状态变量。其他状态变量更具体,NULL_AUDIT 只为特定事件的通知增加这些变量。

system_variables 是一个系统变量元素数组,每个元素都使用 MYSQL_THDVAR_xxx 宏定义。这些系统变量的名称格式为 null_audit_xxx。这些变量可用于在运行时与插件进行通信。

审计插件特定于类型描述符

通用插件描述符中的 audit_null_descriptor 值指向特定于类型的插件描述符。对于审计插件,此描述符具有以下结构(在 plugin_audit.h 中定义)

struct st_mysql_audit
{
  int interface_version;
  void (*release_thd)(MYSQL_THD);
  int (*event_notify)(MYSQL_THD, mysql_event_class_t, const void *);
  unsigned long class_mask[MYSQL_AUDIT_CLASS_MASK_SIZE];
};

审计插件的特定于类型的描述符具有以下成员

  • interface_version:按照惯例,特定于类型的插件描述符以给定插件类型的接口版本开头。服务器在加载插件时会检查 interface_version,以查看插件是否与其兼容。对于审计插件,interface_version 成员的值为 MYSQL_AUDIT_INTERFACE_VERSION(在 plugin_audit.h 中定义)。

  • release_thd:服务器调用此函数以通知插件它正在与线程上下文分离。如果不存在此类函数,则应为 NULL

  • event_notify:服务器调用此函数以通知插件已发生可审计事件。此函数不应为 NULL;这将毫无意义,因为不会发生任何审计。

  • class_mask:一个包含 MYSQL_AUDIT_CLASS_MASK_SIZE 个元素的数组。每个元素都指定给定事件类别的位掩码,以指示插件希望接收通知的子类别。(这是插件 订阅 感兴趣的事件的方式。)如果要忽略对应事件类别的所有事件,则元素应为 0。

服务器使用 event_notifyrelease_thd 函数一起。它们在特定线程的上下文中调用,并且线程可能执行产生多个事件通知的活动。服务器第一次为线程调用 event_notify 时,它会创建一个插件与线程的绑定。在此绑定存在时,无法卸载插件。当线程不再发生任何事件时,服务器会通过调用 release_thd 函数来通知插件,然后销毁绑定。例如,当客户端发出语句时,处理语句的线程可能会通知审计插件有关语句产生的结果集和语句已记录的信息。在这些通知发生后,服务器在将线程置于休眠状态以等待客户端发出另一个语句之前会释放插件。

此设计使插件能够在第一次调用 event_notify 函数时为给定线程分配所需资源,并在 release_thd 函数中释放这些资源

event_notify function:
  if memory is needed to service the thread
    allocate memory
  ... rest of notification processing ...

release_thd function:
  if memory was allocated
    release memory
  ... rest of release processing ...

这比在通知函数中重复分配和释放内存更高效。

对于 NULL_AUDIT 审计插件,特定于类型的插件描述符如下所示

static struct st_mysql_audit audit_null_descriptor=
{
  MYSQL_AUDIT_INTERFACE_VERSION,                    /* interface version    */
  NULL,                                             /* release_thd function */
  audit_null_notify,                                /* notify function      */
  { (unsigned long) MYSQL_AUDIT_GENERAL_ALL,
    (unsigned long) MYSQL_AUDIT_CONNECTION_ALL,
    (unsigned long) MYSQL_AUDIT_PARSE_ALL,
    (unsigned long) MYSQL_AUDIT_AUTHORIZATION_ALL,
    (unsigned long) MYSQL_AUDIT_TABLE_ACCESS_ALL,
    (unsigned long) MYSQL_AUDIT_GLOBAL_VARIABLE_ALL,
    (unsigned long) MYSQL_AUDIT_SERVER_STARTUP_ALL,
    (unsigned long) MYSQL_AUDIT_SERVER_SHUTDOWN_ALL,
    (unsigned long) MYSQL_AUDIT_COMMAND_ALL,
    (unsigned long) MYSQL_AUDIT_QUERY_ALL,
    (unsigned long) MYSQL_AUDIT_STORED_PROGRAM_ALL }
};

服务器调用 audit_null_notify() 将审计事件信息传递给插件。插件没有 release_thd 函数。

class_mask 成员是一个数组,指示插件订阅哪些事件类别。如所示,数组内容订阅了所有可用事件类别的所有子类别。要忽略给定事件类别的所有通知,请将相应的 class_mask 元素指定为 0。

class_mask 元素的数量对应于事件类别的数量,每个事件类别都列在 plugin_audit.h 中定义的 mysql_event_class_t 枚举中

typedef enum
{
  MYSQL_AUDIT_GENERAL_CLASS          = 0,
  MYSQL_AUDIT_CONNECTION_CLASS       = 1,
  MYSQL_AUDIT_PARSE_CLASS            = 2,
  MYSQL_AUDIT_AUTHORIZATION_CLASS    = 3,
  MYSQL_AUDIT_TABLE_ACCESS_CLASS     = 4,
  MYSQL_AUDIT_GLOBAL_VARIABLE_CLASS  = 5,
  MYSQL_AUDIT_SERVER_STARTUP_CLASS   = 6,
  MYSQL_AUDIT_SERVER_SHUTDOWN_CLASS  = 7,
  MYSQL_AUDIT_COMMAND_CLASS          = 8,
  MYSQL_AUDIT_QUERY_CLASS            = 9,
  MYSQL_AUDIT_STORED_PROGRAM_CLASS   = 10,
  /* This item must be last in the list. */
  MYSQL_AUDIT_CLASS_MASK_SIZE
} mysql_event_class_t;

对于任何给定事件类别,plugin_audit.h 都定义了各个事件子类别的位掩码符号,以及一个 xxx_ALL 符号,它是所有子类别位掩码的并集。例如,对于 MYSQL_AUDIT_CONNECTION_CLASS(涵盖连接和断开连接事件的类别),plugin_audit.h 定义了以下符号

typedef enum
{
  /** occurs after authentication phase is completed. */
  MYSQL_AUDIT_CONNECTION_CONNECT          = 1 << 0,
  /** occurs after connection is terminated. */
  MYSQL_AUDIT_CONNECTION_DISCONNECT       = 1 << 1,
  /** occurs after COM_CHANGE_USER RPC is completed. */
  MYSQL_AUDIT_CONNECTION_CHANGE_USER      = 1 << 2,
  /** occurs before authentication. */
  MYSQL_AUDIT_CONNECTION_PRE_AUTHENTICATE = 1 << 3
} mysql_event_connection_subclass_t;

#define MYSQL_AUDIT_CONNECTION_ALL (MYSQL_AUDIT_CONNECTION_CONNECT | \
                                    MYSQL_AUDIT_CONNECTION_DISCONNECT | \
                                    MYSQL_AUDIT_CONNECTION_CHANGE_USER | \
                                    MYSQL_AUDIT_CONNECTION_PRE_AUTHENTICATE)

要订阅连接事件类别 (如 NULL_AUDIT 插件所做) 的所有子类别,插件会在相应的 class_mask 元素(在本例中为 class_mask[1])中指定 MYSQL_AUDIT_CONNECTION_ALL。要只订阅某些子类别,插件会将 class_mask 元素设置为感兴趣的子类别的并集。例如,要只订阅连接和更改用户子类别,插件会将 class_mask[1] 设置为以下值

MYSQL_AUDIT_CONNECTION_CONNECT | MYSQL_AUDIT_CONNECTION_CHANGE_USER

审计插件通知函数

审计插件的大部分工作都在通知函数(特定于类型的插件描述符的 event_notify 成员)中完成。服务器会为每个可审计事件调用此函数。审计插件通知函数具有以下原型

int (*event_notify)(MYSQL_THD, mysql_event_class_t, const void *);

event_notify 函数原型中的第二个和第三个参数分别表示事件类别和指向事件结构的通用指针。 (不同类别中的事件具有不同的结构。 通知函数可以使用事件类别值来确定哪个事件结构适用。)该函数处理事件并返回一个状态,指示服务器应该继续处理事件还是终止它。

对于 NULL_AUDIT,通知函数是 audit_null_notify()。 此函数会增加一个全局事件计数器(插件将其公开为 Audit_null_called 状态值的 value),然后检查事件类别以确定如何处理事件结构。

static int audit_null_notify(MYSQL_THD thd __attribute__((unused)),
                             mysql_event_class_t event_class,
                             const void *event)
{
  ...

  number_of_calls++;

  if (event_class == MYSQL_AUDIT_GENERAL_CLASS)
  {
    const struct mysql_event_general *event_general=
                                    (const struct mysql_event_general *)event;
    ...
  }
  else if (event_class == MYSQL_AUDIT_CONNECTION_CLASS)
  {
    const struct mysql_event_connection *event_connection=
                                (const struct mysql_event_connection *) event;
    ...

  }
  else if (event_class == MYSQL_AUDIT_PARSE_CLASS)
  {
    const struct mysql_event_parse *event_parse =
                                      (const struct mysql_event_parse *)event;
    ...
  }
  ...
}

通知函数根据 event_class 的 value 来解释 event 参数。 event 参数是指向事件记录的通用指针,其结构因事件类别而异。 (plugin_audit.h 文件包含定义每个事件类别内容的结构。)对于每个类别,audit_null_notify() 会将事件强制转换为相应的类别特定结构,然后检查其子类以确定要增加哪个子类计数器。 例如,处理连接事件类别中的事件的代码如下所示

else if (event_class == MYSQL_AUDIT_CONNECTION_CLASS)
{
  const struct mysql_event_connection *event_connection=
                              (const struct mysql_event_connection *) event;

  switch (event_connection->event_subclass)
  {
  case MYSQL_AUDIT_CONNECTION_CONNECT:
    number_of_calls_connection_connect++;
    break;
  case MYSQL_AUDIT_CONNECTION_DISCONNECT:
    number_of_calls_connection_disconnect++;
    break;
  case MYSQL_AUDIT_CONNECTION_CHANGE_USER:
    number_of_calls_connection_change_user++;
    break;
  case MYSQL_AUDIT_CONNECTION_PRE_AUTHENTICATE:
    number_of_calls_connection_pre_authenticate++;
      break;
  default:
    break;
  }
}
注意

通用事件类别 (MYSQL_AUDIT_GENERAL_CLASS) 已弃用,将在未来的 MySQL 版本中删除。 为了减少插件开销,最好只订阅感兴趣的更具体的事件类别。

对于某些事件类别,NULL_AUDIT 插件除了增加计数器之外,还会执行其他处理。 无论如何,当通知函数完成处理事件时,它应该返回一个状态,指示服务器应该继续处理事件还是终止它。

审核插件错误处理

审核插件通知函数可以使用两种方式报告当前事件的状态值

  • 使用通知函数返回值。 在这种情况下,如果服务器应该继续处理事件,则函数返回零;如果服务器应该终止事件,则返回非零值。

  • 调用 my_message() 函数以在从通知函数返回之前设置错误状态。 在这种情况下,通知函数返回值将被忽略,服务器将中止事件并使用错误终止事件处理。 my_message() 参数指示要报告哪个错误及其消息。 例如

    my_message(ER_AUDIT_API_ABORT, "This is my error message.", MYF(0));

    某些事件无法中止。 非零返回值不会被考虑,并且 my_message() 错误调用必须在 is_error() 检查之后进行。 例如

    if (!thd->get_stmt_da()->is_error())
    {
      my_message(ER_AUDIT_API_ABORT, "This is my error message.", MYF(0));
    }

这些事件无法中止

  • MYSQL_AUDIT_CONNECTION_DISCONNECT:服务器无法阻止客户端断开连接。

  • MYSQL_AUDIT_COMMAND_END:此事件提供了已完成执行的命令的状态,因此终止它没有意义。

如果审核插件为不可终止事件返回非零状态,则服务器会忽略该状态并继续处理事件。 如果审核插件使用 my_message() 函数终止不可终止事件,情况也是如此。

审核插件使用

要编译和安装插件库文件,请使用 第 4.4.3 节“编译和安装插件库” 中的说明。 要使库文件可用,请将其安装到插件目录(由 plugin_dir 系统变量指定的目录)中。 对于 NULL_AUDIT 插件,它是在您从源代码构建 MySQL 时进行编译和安装的。 它也包含在二进制发行版中。 构建过程会生成一个名为 adt_null.so 的共享对象库(.so 后缀可能因平台而异)。

要在运行时注册插件,请使用以下语句,根据需要调整平台的 .so 后缀

INSTALL PLUGIN NULL_AUDIT SONAME 'adt_null.so';

有关插件加载的更多信息,请参见 安装和卸载插件

要验证插件安装,请检查 INFORMATION_SCHEMA.PLUGINS 表或使用 SHOW PLUGINS 语句。 请参见 获取服务器插件信息

安装 NULL_AUDIT 审核插件后,它会公开状态变量,指示已调用插件的事件

mysql> SHOW STATUS LIKE 'Audit_null%';
+----------------------------------------+--------+
| Variable_name                          | Value  |
+----------------------------------------+--------+
| Audit_null_authorization_column        | 0      |
| Audit_null_authorization_db            | 0      |
| Audit_null_authorization_procedure     | 0      |
| Audit_null_authorization_proxy         | 0      |
| Audit_null_authorization_table         | 0      |
| Audit_null_authorization_user          | 0      |
| Audit_null_called                      | 185547 |
| Audit_null_command_end                 | 20999  |
| Audit_null_command_start               | 21001  |
| Audit_null_connection_change_user      | 0      |
| Audit_null_connection_connect          | 5823   |
| Audit_null_connection_disconnect       | 5818   |
| Audit_null_connection_pre_authenticate | 5823   |
| Audit_null_general_error               | 1      |
| Audit_null_general_log                 | 26559  |
| Audit_null_general_result              | 19922  |
| Audit_null_general_status              | 21000  |
| Audit_null_global_variable_get         | 0      |
| Audit_null_global_variable_set         | 0      |
| Audit_null_message_internal            | 0      |
| Audit_null_message_user                | 0      |
| Audit_null_parse_postparse             | 14648  |
| Audit_null_parse_preparse              | 14648  |
| Audit_null_query_nested_start          | 6      |
| Audit_null_query_nested_status_end     | 6      |
| Audit_null_query_start                 | 14648  |
| Audit_null_query_status_end            | 14647  |
| Audit_null_server_shutdown             | 0      |
| Audit_null_server_startup              | 1      |
| Audit_null_table_access_delete         | 104    |
| Audit_null_table_access_insert         | 2839   |
| Audit_null_table_access_read           | 97842  |
| Audit_null_table_access_update         | 278    |
+----------------------------------------+--------+

Audit_null_called 统计所有事件,其他变量统计特定事件子类的实例。 例如,前面的 SHOW STATUS 语句会导致服务器将结果发送到客户端并将消息写入通用查询日志(如果该日志已启用)。 因此,发出该语句的客户端会反复导致 Audit_null_calledAudit_null_general_resultAudit_null_general_log 在每次调用时都会增加。 无论该日志是否启用,都会发生通知。

状态变量值是全局的,并且在所有会话中进行汇总。 每个会话都没有计数器。

NULL_AUDIT 公开了一些系统变量,这些变量可以在运行时与插件进行通信

mysql> SHOW VARIABLES LIKE 'null_audit%';
+---------------------------------------------------+-------+
| Variable_name                                     | Value |
+---------------------------------------------------+-------+
| null_audit_abort_message                          |       |
| null_audit_abort_value                            | 1     |
| null_audit_event_order_check                      |       |
| null_audit_event_order_check_consume_ignore_count | 0     |
| null_audit_event_order_check_exact                | 1     |
| null_audit_event_order_started                    | 0     |
| null_audit_event_record                           |       |
| null_audit_event_record_def                       |       |
+---------------------------------------------------+-------+

NULL_AUDIT 系统变量具有以下含义

  • null_audit_abort_message:中止事件时要使用的自定义错误消息。

  • null_audit_abort_value:中止事件时要使用的自定义错误代码。

  • null_audit_event_order_check:在事件匹配之前,预期的事件顺序。 在事件匹配之后,匹配结果。

  • null_audit_event_order_check_consume_ignore_count:事件匹配不应消耗匹配事件的次数。

  • null_audit_event_order_check_exact:事件匹配是否必须完全匹配。 禁用此变量允许在事件顺序匹配期间跳过 null_audit_event_order_check 中未列出的事件。 在指定的事件中,它们仍然必须按照给定的顺序进行匹配。

  • null_audit_event_order_started:供内部使用。

  • null_audit_event_record:事件记录发生后的记录事件。

  • null_audit_event_record_def:记录事件时要匹配的开始和结束事件的名称,用分号隔开。 必须在记录事件的每个语句之前设置该值。

为了演示这些系统变量的使用,假设存在一个表 db1.t1,创建如下

CREATE DATABASE db1;
CREATE TABLE db1.t1 (a VARCHAR(255));

为了测试创建,可以记录通过插件的事件。 要开始记录,请在 null_audit_event_record_def 变量中指定开始和结束事件。 例如

SET @@null_audit_event_record_def =
  'MYSQL_AUDIT_COMMAND_START;MYSQL_AUDIT_COMMAND_END';

在发生与这些开始和结束事件匹配的语句后,null_audit_event_record 系统变量将包含生成的事件序列。 例如,在记录 SELECT 1 语句的事件后,null_audit_event_record 是一个字符串,其 value 包含一组事件字符串

MYSQL_AUDIT_COMMAND_START;command_id="3";
MYSQL_AUDIT_PARSE_PREPARSE;;
MYSQL_AUDIT_PARSE_POSTPARSE;;
MYSQL_AUDIT_GENERAL_LOG;;
MYSQL_AUDIT_QUERY_START;sql_command_id="0";
MYSQL_AUDIT_QUERY_STATUS_END;sql_command_id="0";
MYSQL_AUDIT_GENERAL_RESULT;;
MYSQL_AUDIT_GENERAL_STATUS;;
MYSQL_AUDIT_COMMAND_END;command_id="3";

在记录 INSERT INTO db1.t1 VALUES ('some data') 语句的事件后,null_audit_event_record 将具有以下 value

MYSQL_AUDIT_COMMAND_START;command_id="3";
MYSQL_AUDIT_PARSE_PREPARSE;;
MYSQL_AUDIT_PARSE_POSTPARSE;;
MYSQL_AUDIT_GENERAL_LOG;;
MYSQL_AUDIT_QUERY_START;sql_command_id="5";
MYSQL_AUDIT_TABLE_ACCESS_INSERT;db="db1" table="t1";
MYSQL_AUDIT_QUERY_STATUS_END;sql_command_id="5";
MYSQL_AUDIT_GENERAL_RESULT;;
MYSQL_AUDIT_GENERAL_STATUS;;
MYSQL_AUDIT_COMMAND_END;command_id="3";

每个事件字符串都具有以下格式,用分号隔开字符串部分

event_name;event_data;command

事件字符串具有以下部分

  • event_name:事件名称(以 MYSQL_AUDIT_ 开头的符号)。

  • event_data:为空,或如后面所述,与事件关联的数据。

  • command:为空,或如后面所述,在事件匹配时要执行的命令。

注意

NULL_AUDIT 插件的一个限制是,事件记录仅适用于单个会话。 在您在给定会话中记录事件后,后续会话中的事件记录将产生一个 null_audit_event_record value 为 NULL。 要再次记录事件,必须重新启动插件。

要检查审核 API 调用的顺序,请将 null_audit_event_order_check 变量设置为特定操作的预期事件顺序,列出多个事件字符串,每个事件字符串内部包含两个分号,用额外的分号隔开相邻的事件字符串

event_name;event_data;command [;event_name;event_data;command] ...

例如

SET @@null_audit_event_order_check =
  'MYSQL_AUDIT_CONNECTION_PRE_AUTHENTICATE;;;'
  'MYSQL_AUDIT_GENERAL_LOG;;;'
  'MYSQL_AUDIT_CONNECTION_CONNECT;;';

为了提高可读性,该语句利用了 SQL 语法,将相邻的字符串连接成一个字符串。

在您将 null_audit_event_order_check 变量分配给要匹配的事件列表后,下一个匹配操作将用一个 value 替换变量 value,以指示操作结果

  • 如果成功匹配了预期的事件顺序,则生成的 null_audit_event_order_check value 为 EVENT-ORDER-OK

  • 如果指定的 null_audit_event_order_check value 中止了匹配的事件(如后面所述),则生成的 null_audit_event_order_check value 为 EVENT-ORDER-ABORT

  • 如果预期的事件顺序因意外数据而失败,则生成的 null_audit_event_order_check value 为 EVENT-ORDER-INVALID-DATA。 例如,如果将事件指定为预期会影响表 t1,但实际上影响了 t2,则会发生这种情况。

当您将 null_audit_event_order_check 分配给要匹配的事件列表时,应将某些事件指定为具有非空的 event_data 部分的事件字符串。 下表显示了这些事件的 event_data 格式。 如果事件采用多个数据值,则必须按照显示的顺序指定它们。 或者,可以将 event_data value 指定为 <IGNORE> 以忽略事件数据内容;在这种情况下,事件是否有数据并不重要。

适用事件 事件数据格式

MYSQL_AUDIT_COMMAND_START

MYSQL_AUDIT_COMMAND_END

command_id="id_value"

MYSQL_AUDIT_GLOBAL_VARIABLE_GET

MYSQL_AUDIT_GLOBAL_VARIABLE_SET

name="var_value" value="var_value"

MYSQL_AUDIT_QUERY_NESTED_START

MYSQL_AUDIT_QUERY_NESTED_STATUS_END

MYSQL_AUDIT_QUERY_START

MYSQL_AUDIT_QUERY_STATUS_END

sql_command_id="id_value"

MYSQL_AUDIT_TABLE_ACCESS_DELETE

MYSQL_AUDIT_TABLE_ACCESS_INSERT

MYSQL_AUDIT_TABLE_ACCESS_READ

MYSQL_AUDIT_TABLE_ACCESS_UPDATE

db="db_name" table="table_name"

null_audit_event_order_check value 中,在事件字符串的 command 部分指定 ABORT_RET 可以中止指定事件的审核 API 调用。 (假设该事件是可中止的事件。 以前描述了哪些事件不可中止。)例如,如前所述,这是插入 t1 的预期事件顺序

MYSQL_AUDIT_COMMAND_START;command_id="3";
MYSQL_AUDIT_PARSE_PREPARSE;;
MYSQL_AUDIT_PARSE_POSTPARSE;;
MYSQL_AUDIT_GENERAL_LOG;;
MYSQL_AUDIT_QUERY_START;sql_command_id="5";
MYSQL_AUDIT_TABLE_ACCESS_INSERT;db="db1" table="t1";
MYSQL_AUDIT_QUERY_STATUS_END;sql_command_id="5";
MYSQL_AUDIT_GENERAL_RESULT;;
MYSQL_AUDIT_GENERAL_STATUS;;
MYSQL_AUDIT_COMMAND_END;command_id="3";

要在 MYSQL_AUDIT_QUERY_STATUS_END 事件发生时中止 INSERT 语句执行,请将 null_audit_event_order_check 设置为如下(请记住在相邻的事件字符串之间添加分号分隔符)

SET @@null_audit_event_order_check =
  'MYSQL_AUDIT_COMMAND_START;command_id="3";;'
  'MYSQL_AUDIT_PARSE_PREPARSE;;;'
  'MYSQL_AUDIT_PARSE_POSTPARSE;;;'
  'MYSQL_AUDIT_GENERAL_LOG;;;'
  'MYSQL_AUDIT_QUERY_START;sql_command_id="5";;'
  'MYSQL_AUDIT_TABLE_ACCESS_INSERT;db="db1" table="t1";;'
  'MYSQL_AUDIT_QUERY_STATUS_END;sql_command_id="5";ABORT_RET';

没有必要列出预期在包含 command value 为 ABORT_RET 的事件字符串之后发生的事件。

在审核插件匹配了前面的序列后,它将中止事件处理并将错误消息发送到客户端。 它还会将 null_audit_event_order_check 设置为 EVENT-ORDER-ABORT

mysql> INSERT INTO db1.t1 VALUES ('some data');
ERROR 3164 (HY000): Aborted by Audit API ('MYSQL_AUDIT_QUERY_STATUS_END';1).
mysql> SELECT @@null_audit_event_order_check;
+--------------------------------+
| @@null_audit_event_order_check |
+--------------------------------+
| EVENT-ORDER-ABORT              |
+--------------------------------+

从审核 API 通知例程返回非零 value 是中止事件执行的标准方法。 也可以通过将 null_audit_abort_value 变量设置为通知例程应返回的 value 来指定自定义错误代码

SET @@null_audit_abort_value = 123;

中止序列会导致包含自定义错误代码的标准消息。假设您设置审核日志系统变量如下,以便在匹配针对SELECT 1 语句发生的事件时中止

SET @@null_audit_abort_value = 123;
SET @@null_audit_event_order_check =
  'MYSQL_AUDIT_COMMAND_START;command_id="3";;'
  'MYSQL_AUDIT_PARSE_PREPARSE;;;'
  'MYSQL_AUDIT_PARSE_POSTPARSE;;;'
  'MYSQL_AUDIT_GENERAL_LOG;;;'
  'MYSQL_AUDIT_QUERY_START;sql_command_id="0";ABORT_RET';

那么执行SELECT 1 会导致包含自定义错误代码的此错误消息

mysql> SELECT 1;
ERROR 3164 (HY000): Aborted by Audit API ('MYSQL_AUDIT_QUERY_START';123).

mysql> SELECT @@null_audit_event_order_check;
+--------------------------------+
| @@null_audit_event_order_check |
+--------------------------------+
| EVENT-ORDER-ABORT              |
+--------------------------------+

也可以使用自定义消息中止事件,该消息通过设置null_audit_abort_message 变量指定。假设您设置审核日志系统变量如下

SET @@null_audit_abort_message = 'Custom error text.';
SET @@null_audit_event_order_check =
  'MYSQL_AUDIT_COMMAND_START;command_id="3";;'
  'MYSQL_AUDIT_PARSE_PREPARSE;;;'
  'MYSQL_AUDIT_PARSE_POSTPARSE;;;'
  'MYSQL_AUDIT_GENERAL_LOG;;;'
  'MYSQL_AUDIT_QUERY_START;sql_command_id="0";ABORT_RET';

那么中止序列会导致以下错误消息

mysql> SELECT 1;
ERROR 3164 (HY000): Custom error text.
mysql> SELECT @@null_audit_event_order_check;
+--------------------------------+
| @@null_audit_event_order_check |
+--------------------------------+
| EVENT-ORDER-ABORT              |
+--------------------------------+

要禁用NULL_AUDIT 插件(在测试后),请使用此语句将其卸载

UNINSTALL PLUGIN NULL_AUDIT;