MySQL8 中文参考(二十三)(4)https://developer.aliyun.com/article/1566148
8.1.2.1 密码安全的最终用户准则
MySQL 用户应该遵循以下准则来保护密码安全。
当您运行客户端程序连接到 MySQL 服务器时,不建议以暴露给其他用户发现的方式指定密码。您运行客户端程序时可以使用的指定密码的方法在此列出,以及每种方法的风险评估。简而言之,最安全的方法是让客户端程序提示输入密码或在正确保护的选项文件中指定密码。
- 使用mysql_config_editor实用程序,它允许您将身份验证凭据存储在名为
.mylogin.cnf
的加密登录路径文件中。稍后,MySQL 客户端程序可以读取该文件以获取连接到 MySQL 服务器的身份验证凭据。请参阅 Section 6.6.7, “mysql_config_editor — MySQL Configuration Utility”。 - 在命令行上使用
--password=*
password*
或-p*
password*
选项。例如:
$> mysql -u francis -pfrank *db_name*
- 警告
这很方便但不安全。在某些系统上,您的密码会对系统状态程序(如ps)可见,其他用户可以调用这些程序来显示命令行。MySQL 客户端通常在初始化序列期间用零覆盖命令行密码参数。然而,在这个值可见的瞬间仍然存在。此外,在某些系统上,这种覆盖策略是无效的,密码仍然对ps可见。(SystemV Unix 系统和其他系统可能存在这个问题。)
如果您的操作环境设置为在终端窗口的标题栏中显示当前命令,则只要命令正在运行,密码就会保持可见,即使命令已经在窗口内容区域滚动出视野。 - 在命令行上使用
--password
或-p
选项,不指定密码值。在这种情况下,客户端程序会交互式地请求密码:
$> mysql -u francis -p *db_name* Enter password: ********
*
字符表示您输入密码的位置。在输入时不会显示密码。
以这种方式输入密码比在命令行上指定密码更安全,因为其他用户看不到密码。然而,这种输入密码的方法只适用于您交互式运行的程序。如果您想从非交互式脚本中调用客户端,那么就没有机会从键盘输入密码。在某些系统上,您甚至可能发现脚本的第一行被读取并解释(错误地)为您的密码。- 将密码存储在一个选项文件中。例如,在 Unix 上,您可以在主目录的
.my.cnf
文件的[client]
部分列出您的密码:
[client] password=*password*
- 为了保护密码安全,该文件不应该对任何人开放访问,只能由您自己访问。为了确保这一点,将文件访问模式设置为
400
或600
。例如:
$> chmod 600 .my.cnf
- 从命令行命名包含密码的特定选项文件,使用
--defaults-file=*
file_name*
选项,其中file_name
是文件的完整路径名。例如:
$> mysql --defaults-file=/home/francis/mysql-opts
- Section 6.2.2.2, “Using Option Files”详细讨论了选项文件。
在 Unix 系统上,mysql客户端会将执行的语句记录到一个历史文件中(参见 Section 6.5.1.3, “mysql Client Logging”)。默认情况下,该文件名为.mysql_history
,并创建在您的主目录中。密码可以以明文形式写入 SQL 语句,比如CREATE USER
和ALTER USER
,因此如果您使用这些语句,它们会被记录在历史文件中。为了保护这个文件,使用限制性的访问模式,与之前描述的.my.cnf
文件相同。
如果您的命令解释器维护一个历史记录,保存命令的任何文件都包含在命令行中输入的 MySQL 密码。例如,bash使用~/.bash_history
。任何这样的文件都应该具有限制性的访问模式。
原文:
dev.mysql.com/doc/refman/8.0/en/password-security-admin.html
8.1.2.2 密码安全的管理员准则
数据库管理员应遵循以下准则以保护密码安全。
MySQL 将用户账户的密码存储在mysql.user
系统表中。绝对不应该将对该表的访问权限授予任何非管理员账户。
可以设置账户密码过期,以便用户必须重置密码。参见第 8.2.15 节,“密码管理”,以及第 8.2.16 节,“服务器处理过期密码”。
可以使用validate_password
插件来强制执行可接受密码的策略。参见第 8.4.3 节,“密码验证组件”。
一个用户如果有权限修改插件目录(plugin_dir
系统变量的值)或指定插件目录位置的my.cnf
文件,就可以替换插件并修改插件提供的功能,包括认证插件。
应该保护可能写入密码的文件,比如日志文件。参见第 8.1.2.3 节,“密码和日志记录”。
8.1.2.3 密码和日志记录
密码可以以明文形式写入 SQL 语句,例如CREATE USER
、GRANT
和SET PASSWORD
。如果 MySQL 服务器按原样记录这些语句,其中的密码将对任何具有日志访问权限的人可见。
语句记录避免以明文形式写入以下语句的密码:
CREATE USER ... IDENTIFIED BY ... ALTER USER ... IDENTIFIED BY ... SET PASSWORD ... START SLAVE ... PASSWORD = ... START REPLICA ... PASSWORD = ... CREATE SERVER ... OPTIONS(... PASSWORD ...) ALTER SERVER ... OPTIONS(... PASSWORD ...)
这些语句中的密码被重写,以便在写入一般查询日志、慢查询日志和二进制日志的语句文本中不会直接显示。重写不适用于其他语句。特别是,对于引用明文密码的mysql.user
系统表的INSERT
或UPDATE
语句,将按原样记录,因此应避免使用这些语句。(直接修改授权表是不鼓励的。)
对于一般的查询日志,可以通过使用--log-raw
选项启动服务器来抑制密码重写。出于安全原因,不建议在生产环境中使用此选项。出于诊断目的,查看服务器接收到的语句的确切文本可能是有用的。
默认情况下,由审计日志插件生成的审计日志文件的内容未加密,可能包含敏感信息,例如 SQL 语句的文本。出于安全原因,审计日志文件应写入仅对 MySQL 服务器和有合法查看日志原因的用户可访问的目录。参见第 8.4.5.3 节,“MySQL 企业审计安全注意事项”。
如果安装了查询重写插件(参见查询重写插件),则服务器接收到的语句可能会被重写。在这种情况下,--log-raw
选项会影响语句记录如下:
- 没有
--log-raw
,服务器记录由查询重写插件返回的语句。这可能与接收到的语句不同。 - 使用
--log-raw
,服务器记录接收到的原始语句。
密码重写的一个影响是,无法解析的语句(例如由于语法错误)不会写入一般查询日志,因为无法确定是否不包含密码。需要记录所有语句(包括出现错误的语句)的用例应使用--log-raw
选项,但要注意这也会绕过密码重写。
当期望明文密码时,密码重写仅会发生。对于期望密码哈希值的语法语句,不会进行重写。如果错误地为此类语法提供了明文密码,则密码将被记录为给定的形式,而不进行重写。
为了保护日志文件免受未经授权的暴露,将它们放在一个限制服务器和数据库管理员访问的目录中。如果服务器将日志记录到mysql
数据库中的表中,请仅授予数据库管理员对这些表的访问权限。
复制品将复制源服务器的密码存储在它们的连接元数据存储库中,默认情况下是mysql
数据库中名为slave_master_info
的表。现在已不推荐使用数据目录中的文件作为连接元数据存储库,但仍然可能(参见第 19.2.4 节,“中继日志和复制元数据存储库”)。确保连接元数据存储库只能由数据库管理员访问。将密码存储在连接元数据存储库中的替代方法是使用START REPLICA
(或在 MySQL 8.0.22 之前,START SLAVE
)或START GROUP_REPLICATION
语句指定连接到源的凭据。
使用受限访问模式保护包含日志表或包含密码的日志文件的数据库备份。
8.1.3 使 MySQL 免受攻击者攻击
译文:
dev.mysql.com/doc/refman/8.0/en/security-against-attack.html
当连接到 MySQL 服务器时,应该使用密码。密码不会以明文形式通过连接传输。
所有其他信息都以文本形式传输,可以被能够监视连接的任何人读取。如果客户端和服务器之间的连接经过不受信任的网络,并且您对此感到担忧,您可以使用压缩协议使流量更加难以解密。您还可以使用 MySQL 的内部 SSL 支持使连接更加安全。请参见 Section 8.3, “Using Encrypted Connections”。或者,使用 SSH 在 MySQL 服务器和 MySQL 客户端之间建立加密的 TCP/IP 连接。您可以在www.openssh.org/
找到一个开源 SSH 客户端,并在en.wikipedia.org/wiki/Comparison_of_SSH_clients
找到开源和商业 SSH 客户端的比较。
要使 MySQL 系统安全,您应该强烈考虑以下建议:
- 要求所有 MySQL 账户都有密码。客户端程序不一定知道运行它的人的身份。对于客户端/服务器应用程序来说,用户可以向客户端程序指定任何用户名是很常见的。例如,任何人都可以使用mysql程序连接为任何其他人,只需通过
mysql -u *
other_user* *
db_name*
来调用,如果*other_user
*没有密码。如果所有账户都有密码,使用另一个用户的账户连接就变得更加困难。
有关设置密码方法的讨论,请参见 Section 8.2.14, “Assigning Account Passwords”。 - 确保在数据库目录中具有读取或写入权限的唯一 Unix 用户帐户是用于运行mysqld的帐户。
- 永远不要将 MySQL 服务器作为 Unix
root
用户运行。这是极其危险的,因为任何具有FILE
权限的用户都可以使服务器以root
身份创建文件(例如,~root/.bashrc
)。为了防止这种情况,mysqld拒绝以root
身份运行,除非明确使用--user=root
选项指定。
mysqld可以(而且应该)作为一个普通的、非特权用户运行。您可以创建一个名为mysql
的单独的 Unix 帐户,以使一切更加安全。仅使用此帐户来管理 MySQL。要将mysqld作为不同的 Unix 用户启动,请在指定服务器选项的my.cnf
选项文件的[mysqld]
组中添加一个指定用户名称的user
选项。例如:
[mysqld] user=mysql
- 这将导致服务器作为指定用户启动,无论您是手动启动还是使用mysqld_safe或mysql.server启动。有关更多详细信息,请参见 Section 8.1.5,“如何将 MySQL 作为普通用户运行”。
将mysqld作为root
以外的 Unix 用户运行并不意味着您需要更改user
表中的root
用户名。MySQL 帐户的用户名与 Unix 帐户的用户名无关。 - 不要将
FILE
权限授予非管理员用户。拥有此权限的任何用户都可以以mysqld守护程序的权限在文件系统中的任何位置写入文件。这包括包含实现权限表的文件的服务器数据目录。为了使具有FILE
权限的操作更安全一些,使用SELECT ... INTO OUTFILE
生成的文件不会覆盖现有文件,并且可以被所有人写入。FILE
权限也可以用于读取任何对所有用户可读或对服务器运行的 Unix 用户可访问的文件。有了这个权限,您可以将任何文件读入数据库表中。例如,可以通过使用LOAD DATA
将/etc/passwd
加载到表中,然后可以使用SELECT
显示。
为了限制可以读取和写入文件的位置,将secure_file_priv
系统设置为特定目录。请参见 Section 7.1.8,“服务器系统变量”。 - 对二进制日志文件和中继日志文件进行加密。加密有助于保护这些文件和其中可能包含的敏感数据,防止外部攻击者滥用,也防止存储它们的操作系统用户未经授权查看。您可以通过将
binlog_encryption
系统变量设置为ON
来在 MySQL 服务器上启用加密。有关更多信息,请参见第 19.3.2 节,“加密二进制日志文件和中继日志文件”。 - 不要向非管理员用户授予
PROCESS
或SUPER
权限。mysqladmin processlist和SHOW PROCESSLIST
的输出显示当前正在执行的任何语句的文本,因此任何被允许查看服务器进程列表的用户可能能够看到其他用户发出的语句。
mysqld为具有CONNECTION_ADMIN
或SUPER
权限的用户保留了额外的连接,以便 MySQLroot
用户可以登录并检查服务器活动,即使所有正常连接都在使用中。SUPER
权限可用于终止客户端连接,通过更改系统变量的值改变服务器操作,并控制复制服务器。 - 不要允许使用符号链接到表格。 (可以使用
--skip-symbolic-links
选项禁用此功能。)如果您以root
身份运行mysqld,那么任何具有对服务器数据目录的写访问权限的人都可以删除系统中的任何文件!请参见第 10.12.2.2 节,“在 Unix 上为 MyISAM 表使用符号链接”。 - 存储过程和视图应该按照第 27.6 节,“存储对象访问控制”中讨论的安全准则编写。
- 如果您不信任您的 DNS,应该在授权表中使用 IP 地址而不是主机名。无论如何,您都应该非常小心地创建包含通配符的主机名值的授权表条目。
- 如果您想限制单个帐户允许的连接数,可以通过在mysqld中设置
max_user_connections
变量来实现。CREATE USER
和ALTER USER
语句还支持资源控制选项,用于限制帐户允许使用服务器的范围。请参阅 Section 15.7.1.3, “CREATE USER Statement”和 Section 15.7.1.1, “ALTER USER Statement”。 - 如果插件目录对服务器是可写的,用户可能会使用
SELECT ... INTO DUMPFILE
将可执行代码写入目录中的文件。可以通过将plugin_dir
设置为服务器只读或将secure_file_priv
设置为SELECT
可以安全写入的目录来防止这种情况发生。
8.1.4 与安全相关的 mysqld 选项和变量
以下表格显示了影响安全性的mysqld选项和系统变量。有关每个选项的描述,请参见第 7.1.7 节,“服务器命令选项”和第 7.1.8 节,“服务器系统变量”。
表 8.1 安全选项和变量摘要
名称 | 命令行 | 选项文件 | 系统变量 | 状态变量 | 变量范围 | 动态 |
allow-suspicious-udfs | 是 | 是 | ||||
automatic_sp_privileges | 是 | 是 | 是 | 全局 | 是 | |
chroot | 是 | 是 | ||||
local_infile | 是 | 是 | 是 | 全局 | 是 | |
safe-user-create | 是 | 是 | ||||
secure_file_priv | 是 | 是 | 是 | 全局 | 否 | |
skip-grant-tables | 是 | 是 | ||||
skip_name_resolve | 是 | 是 | 是 | 全局 | 否 | |
skip_networking | 是 | 是 | 是 | 全局 | 否 | |
skip_show_database | 是 | 是 | 是 | 全局 | 否 | |
名称 | 命令行 | 选项文件 | 系统变量 | 状态变量 | 变量范围 | 动态 |
8.1.5 如何以普通用户身份运行 MySQL
在 Windows 上,您可以使用普通用户帐户将服务器作为 Windows 服务运行。
在 Linux 上,如果使用 MySQL 存储库或 RPM 软件包进行安装,则应该由本地mysql
操作系统用户启动 MySQL 服务器mysqld。使用其他操作系统用户启动不受 MySQL 存储库包含的 init 脚本支持。
在 Unix(或 Linux 使用tar.gz
软件包进行安装时),MySQL 服务器mysqld可以由任何用户启动和运行。但是,出于安全原因,您应避免以 Unix root
用户身份运行服务器。要将mysqld更改为以普通非特权 Unix 用户*user_name
*运行,您必须执行以下操作:
- 如果服务器正在运行,请停止它(使用mysqladmin shutdown)。
- 更改数据库目录和文件,以便*
user_name
*有权限在其中读取和写入文件(您可能需要以 Unixroot
用户的身份执行此操作):
$> chown -R *user_name* */path/to/mysql/datadir*
- 如果不这样做,服务器在以*
user_name
*运行时无法访问数据库或表。
如果 MySQL 数据目录中的目录或文件是符号链接,则chown -R
可能不会为您跟随符号链接。如果没有,请您也必须跟随这些链接并更改它们指向的目录和文件。 - 以用户*
user_name
启动服务器。另一种选择是以 Unixroot
用户启动mysqld,并使用--user=*
user_name*
选项。mysqld启动后,然后在接受任何连接之前切换为以 Unix 用户user_name
*运行。 - 要在系统启动时自动以给定用户启动服务器,请通过在
/etc/my.cnf
选项文件的[mysqld]
组或服务器数据目录中的my.cnf
选项文件中添加user
选项来指定用户名。例如:
[mysqld] user=*user_name*
如果您的 Unix 机器本身没有受到保护,您应该在授权表中为 MySQL root
帐户分配密码。否则,任何在该机器上具有登录帐户的用户都可以使用--user=root
选项运行mysql客户端并执行任何操作。(在任何情况下为 MySQL 帐户分配密码都是个好主意,但特别是在服务器主机上存在其他登录帐户时更是如此。)请参见 Section 2.9.4, “Securing the Initial MySQL Account”。
8.1.6 LOAD DATA LOCAL
的安全考虑
原文:
dev.mysql.com/doc/refman/8.0/en/load-data-local-security.html
LOAD DATA
语句将数据文件加载到表中。该语句可以加载位于服务器主机上的文件,或者如果指定了LOCAL
关键字,则可以加载位于客户端主机上的文件。
LOAD DATA
的LOCAL
版本存在两个潜在的安全问题:
- 因为
LOAD DATA LOCAL
是一个 SQL 语句,解析发生在服务器端,并且文件从客户端主机传输到服务器主机是由 MySQL 服务器发起的,MySQL 服务器告诉客户端语句中命名的文件。理论上,一个经过修补的服务器可以告诉客户端程序传输服务器选择的文件,而不是语句中命名的文件。这样的服务器可以访问客户端用户具有读取访问权限的任何文件。(事实上,一个经过修补的服务器实际上可以对任何语句回复文件传输请求,而不仅仅是LOAD DATA LOCAL
,因此更根本的问题是客户端不应连接到不受信任的服务器。) - 在 Web 环境中,客户端是从 Web 服务器连接的,用户可以使用
LOAD DATA LOCAL
读取 Web 服务器进程具有读取访问权限的任何文件(假设用户可以对 SQL 服务器运行任何语句)。在这种环境中,客户端实际上是 Web 服务器,而不是由连接到 Web 服务器的用户运行的远程程序。
为了避免连接到不受信任的服务器,客户端可以通过使用--ssl-mode=VERIFY_IDENTITY
选项和适当的 CA 证书建立安全连接并验证服务器身份。要实现这种级别的验证,您必须首先确保服务器的 CA 证书可靠地可供副本使用,否则将导致可用性问题。有关更多信息,请参见加密连接的命令选项。
为了避免LOAD DATA
问题,客户端应避免使用LOCAL
,除非已采取适当的客户端端预防措施。
为了控制本地数据加载,MySQL 允许启用或禁用该功能。此外,从 MySQL 8.0.21 开始,MySQL 使客户端能够将本地数据加载操作限制为位于指定目录中的文件。
- 启用或禁用本地数据加载功能
- 限制本地数据加载的文件
- MySQL Shell 和本地数据加载
启用或禁用本地数据加载功能
管理员和应用程序可以配置是否允许本地数据加载如下:
- 在服务器端:
local_infile
系统变量控制服务器端的LOCAL
功能。根据local_infile
设置,服务器拒绝或允许请求本地数据加载的客户端的本地数据加载。- 默认情况下,
local_infile
被禁用。(这是与 MySQL 先前版本的更改。)为了使服务器明确拒绝或允许LOAD DATA LOCAL
语句(无论客户端程序和库在构建时或运行时如何配置),请使用禁用或启用local_infile
启动mysqld。local_infile
也可以在运行时设置。
- 在客户端:
ENABLED_LOCAL_INFILE
CMake选项控制 MySQL 客户端库的编译默认LOCAL
功能(参见 Section 2.8.7,“MySQL 源配置选项”)。因此,未做明确安排的客户端根据 MySQL 构建时指定的ENABLED_LOCAL_INFILE
设置,LOCAL
功能被禁用或启用。- 默认情况下,MySQL 二进制发行版中的客户端库编译时禁用了
ENABLED_LOCAL_INFILE
。如果从源代码编译 MySQL,请根据未做明确安排的客户端是否应该禁用或启用LOCAL
功能,配置ENABLED_LOCAL_INFILE
为禁用或启用。 - 对于使用 C API 的客户端程序,本地数据加载功能取决于编译到 MySQL 客户端库中的默认值。要显式启用或禁用它,请调用
mysql_options()
C API 函数来禁用或启用MYSQL_OPT_LOCAL_INFILE
选项。参见 mysql_options()。 - 对于mysql客户端,本地数据加载功能由默认编译到 MySQL 客户端库中。要显式禁用或启用它,请使用
--local-infile=0
或--local-infile[=1]
选项。 - 对于mysqlimport客户端,默认情况下不使用本地数据加载。要显式禁用或启用它,请使用
--local=0
或--local[=1]
选项。 - 如果您在读取选项文件中的
[client]
组的 Perl 脚本或其他程序中使用LOAD DATA LOCAL
,您可以向该组添加一个local-infile
选项设置。为了防止不理解此选项的程序出现问题,请使用loose-
前缀指定它:
[client] loose-local-infile=0
- 或:
[client] loose-local-infile=1
- 在所有情况下,客户端成功使用
LOCAL
加载操作还需要服务器允许本地加载。
如果LOCAL
功能被禁用,无论是在服务器端还是客户端端,尝试发出LOAD DATA LOCAL
语句的客户端将收到以下错误消息:
ERROR 3950 (42000): Loading local data is disabled; this must be enabled on both the client and server side
限制允许用于本地数据加载的文件
截至 MySQL 8.0.21,MySQL 客户端库使客户端应用程序能够将本地数据加载操作限制为位于指定目录中的文件。某些 MySQL 客户端程序利用了这一功能。
使用 C API 的客户端程序可以通过mysql_options()
C API 函数的MYSQL_OPT_LOCAL_INFILE
和MYSQL_OPT_LOAD_DATA_LOCAL_DIR
选项来控制允许加载数据的文件(参见 mysql_options())。
MYSQL_OPT_LOAD_DATA_LOCAL_DIR
的效果取决于LOCAL
数据加载是启用还是禁用的:
- 如果通过默认在 MySQL 客户端库中或显式启用
MYSQL_OPT_LOCAL_INFILE
来启用LOCAL
数据加载,则MYSQL_OPT_LOAD_DATA_LOCAL_DIR
选项不起作用。 - 如果通过默认在 MySQL 客户端库中或显式禁用
MYSQL_OPT_LOCAL_INFILE
来禁用LOCAL
数据加载,则可以使用MYSQL_OPT_LOAD_DATA_LOCAL_DIR
选项指定允许本地加载文件的目录。在这种情况下,LOCAL
数据加载被允许但仅限于位于指定目录中的文件。MYSQL_OPT_LOAD_DATA_LOCAL_DIR
值的解释如下:
- 如果该值为空指针(默认值),则不指定任何目录,结果是不允许任何文件进行
LOCAL
数据加载。 - 如果值是目录路径名,则允许
LOCAL
数据加载,但仅限于位于指定目录中的文件。无论基础文件系统的大小写敏感性如何,目录路径名和要加载的文件的路径名的比较都是区分大小写的。
MySQL 客户端程序如下使用前述的mysql_options()
选项:
- mysql客户端有一个
--load-data-local-dir
选项,接受一个目录路径或空字符串。mysql使用该选项值来设置MYSQL_OPT_LOAD_DATA_LOCAL_DIR
选项(将空字符串设置为 null 指针)。--load-data-local-dir
的效果取决于是否启用了LOCAL
数据加载:
- 如果启用了
LOCAL
数据加载,无论是在 MySQL 客户端库中默认设置,还是通过指定--local-infile[=1]
,都会忽略--load-data-local-dir
选项。 - 如果
LOCAL
数据加载被禁用,无论是在 MySQL 客户端库中默认设置,还是通过指定--local-infile=0
,都会应用--load-data-local-dir
选项。
- 当应用
--load-data-local-dir
时,选项值指定了本地数据文件必须位于的目录。无论基础文件系统的大小写敏感性如何,目录路径名和要加载的文件的路径名的比较都是区分大小写的。如果选项值为空字符串,则不指定任何目录,结果是不允许进行本地数据加载。 - mysqlimport为其处理的每个文件设置
MYSQL_OPT_LOAD_DATA_LOCAL_DIR
,以便包含文件的目录是允许的本地加载目录。 - 对应于
LOAD DATA
语句的数据加载操作,mysqlbinlog从二进制日志事件中提取文件,将它们写入本地文件系统作为临时文件,并写入LOAD DATA LOCAL
语句以使文件被加载。默认情况下,mysqlbinlog将这些临时文件写入操作系统特定的目录。可以使用--local-load
选项来明确指定mysqlbinlog应准备本地临时文件的目录。
因为其他进程可以将文件写入默认的系统特定目录,建议指定--local-load
选项给mysqlbinlog,以指定不同的目录用于数据文件,然后通过在处理来自mysqlbinlog的输出时指定--load-data-local-dir
选项给mysql来指定相同的目录。
MySQL Shell 和本地数据加载
MySQL Shell 提供了许多实用程序来转储表、模式或服务器实例,并将它们加载到其他实例中。当您使用这些实用程序处理数据时,MySQL Shell 提供额外的功能,如输入预处理、多线程并行加载、文件压缩和解压缩,以及处理访问 Oracle Cloud Infrastructure 对象存储桶的功能。为了获得最佳功能,请始终使用 MySQL Shell 的最新版本的转储和加载实用程序。
MySQL Shell 的数据上传实用程序使用LOAD DATA LOCAL INFILE
语句来上传数据,因此目标服务器实例上必须将local_infile
系统变量设置为ON
。您可以在上传数据之前执行此操作,然后再次将其删除。这些实用程序安全地处理文件传输请求,以处理本主题中讨论的安全考虑。
MySQL Shell 包括以下转储和加载实用程序:
表导出工具 util.exportTable()
将 MySQL 关系表导出为数据文件,可以使用 MySQL Shell 的并行表导入实用程序上传到 MySQL 服务器实例,导入到不同的应用程序,或用作逻辑备份。该实用程序具有预设选项和自定义选项,可生成不同的输出格式。
并行表导入实用程序 util.importTable()
将数据文件导入到 MySQL 关系表中。数据文件可以是 MySQL Shell 的表导出实用程序的输出,也可以是实用程序的预设和自定义选项支持的其他格式。该实用程序可以在将数据添加到表之前进行输入预处理。它可以接受多个数据文件合并到单个关系表中,并自动解压缩压缩文件。
实例转储实用程序 util.dumpInstance()
,模式转储实用程序 util.dumpSchemas()
和表转储实用程序 util.dumpTables()
将实例、模式或表导出为一组转储文件,然后可以使用 MySQL Shell 的转储加载实用程序将其上传到 MySQL 实例。这些实用程序提供 Oracle Cloud 基础设施对象存储流、MySQL HeatWave 服务兼容性检查和修改,并能够进行干跑以在继续进行转储之前识别问题。
转储加载实用程序 util.loadDump()
使用 MySQL Shell 的实例、模式或表转储实用程序创建的转储文件导入到 MySQL HeatWave 服务 DB 系统或 MySQL 服务器实例中。该实用程序管理上传过程,并提供从远程存储的数据流、表或表块的并行加载、进度状态跟踪、恢复和重置功能,以及在转储仍在进行时进行并发加载的选项。MySQL Shell 的并行表导入实用程序可以与转储加载实用程序结合使用,在将数据上传到目标 MySQL 实例之前修改数据。
有关实用程序的详细信息,请参见 MySQL Shell 实用程序。
8.1.7 客户端编程安全准则
原文:
dev.mysql.com/doc/refman/8.0/en/secure-client-programming.html
访问 MySQL 的客户端应用程序应遵循以下准则,以避免错误解释外部数据或暴露敏感信息。
- 正确处理外部数据
- 正确处理 MySQL 错误消息
正确处理外部数据
访问 MySQL 的应用程序不应信任用户输入的任何数据,用户可以尝试通过在网络表单、URL 或您构建的任何应用程序中输入特殊或转义字符序列来欺骗您的代码。确保如果用户尝试通过在表单中输入类似;删除数据库 mysql;
的内容来执行 SQL 注入,您的应用程序仍然保持安全。这是一个极端的例子,但如果您不为此做好准备,可能会发生大规模的安全漏洞和数据丢失。
一个常见的错误是仅保护字符串数据值。请记得检查数值数据。如果一个应用程序生成类似SELECT * FROM table WHERE ID=234
的查询,当用户输入值234
时,用户可以输入值234 OR 1=1
来导致应用程序生成查询SELECT * FROM table WHERE ID=234 OR 1=1
。结果,服务器检索表中的每一行。这暴露了每一行并导致服务器负载过大。保护免受此类攻击的最简单方法是在数值常量周围使用单引号:SELECT * FROM table WHERE ID='234'
。如果用户输入额外信息,它都将成为字符串的一部分。在数值上下文中,MySQL 会自动将此字符串转换为数字并剥离其中的任何尾随非数字字符。
有时人们认为如果数据库只包含公开可用的数据,则无需保护。这是不正确的。即使可以显示数据库中的任何行,您仍应防范拒绝服务攻击(例如,基于前一段中导致服务器浪费资源的技术)。否则,您的服务器将对合法用户不响应。
检查清单:
- 启用严格的 SQL 模式,告诉服务器更严格地接受哪些数据值。请参阅第 7.1.11 节,“服务器 SQL 模式”。
- 尝试在所有的网络表单中输入单引号和双引号(
'
和"
)。如果出现任何 MySQL 错误,请立即调查问题。 - 尝试通过向动态 URL 添加
%22
("
)、%23
(#
)和%27
('
)来修改它们。 - 尝试将动态 URL 中的数据类型从数字类型修改为字符类型,使用前面示例中显示的字符。您的应用程序应对这些攻击和类似攻击保持安全。
- 尝试在数字字段中输入字符、空格和特殊符号而不是数字。您的应用程序应在传递给 MySQL 之前将它们删除,否则会生成错误。将未经检查的值传递给 MySQL 非常危险!
- 在将数据传递给 MySQL 之前检查数据的大小。
- 让应用程序使用与您用于管理目的不同的用户名连接到数据库。不要给予应用程序任何不需要的访问权限。
许多应用程序编程接口提供了一种方法来转义数据值中的特殊字符。正确使用可以防止应用程序用户输入导致应用程序生成具有不同效果的语句:
- MySQL SQL 语句:使用 SQL 预处理语句,并仅通过占位符接受数据值;参见 第 15.5 节,“预处理语句”。
- MySQL C API:使用
mysql_real_escape_string_quote()
API 调用。或者,使用 C API 预处理语句接口,并仅通过占位符接受数据值;参见 C API 预处理语句接口。 - MySQL++:对查询流使用
escape
和quote
修饰符。 - PHP:使用
mysqli
或pdo_mysql
扩展,而不是旧的ext/mysql
扩展。首选的 API 支持改进的 MySQL 认证协议和密码,以及带有占位符的预处理语句。另请参阅 MySQL 和 PHP。
如果必须使用旧的ext/mysql
扩展,则用mysql_real_escape_string_quote()
函数进行转义,而不是mysql_escape_string()
或addslashes()
,因为只有mysql_real_escape_string_quote()
是字符集感知的;在使用(无效的)多字节字符集时,其他函数可能会被“绕过”。 - Perl DBI:使用占位符或
quote()
方法。 - Java JDBC:使用
PreparedStatement
对象和占位符。
其他编程接口可能具有类似的功能。
适当处理 MySQL 错误消息
应用程序有责任拦截由执行与 MySQL 数据库服务器的 SQL 语句导致的错误,并适当处理这些错误。
MySQL 错误返回的信息并非毫无意义,因为这些信息对于使用应用程序调试 MySQL 至关重要。例如,要调试一个常见的 10 路连接SELECT
语句,如果不提供涉及问题的数据库、表和其他对象的信息,几乎是不可能的。因此,MySQL 错误有时必须包含对这些对象名称的引用。
当应用程序从 MySQL 接收到这样的错误时,一个简单但不安全的方法是拦截并将其原样显示给客户端。然而,揭示错误信息是一种已知的应用程序漏洞类型(CWE-209),应用程序开发人员必须确保应用程序没有这种漏洞。
例如,一个显示如下消息的应用程序向客户端暴露了数据库名称和表名称,这是客户端可能会尝试利用的信息:
ERROR 1146 (42S02): Table 'mydb.mytable' doesn't exist
相反,当应用程序从 MySQL 接收到这样的错误时,正确的行为是记录适当的信息,包括错误信息,仅供信任人员访问的安全审计位置。应用程序可以向用户返回更通用的信息,比如“内部错误”。