文章目录
一般安全问题
安全指南
在连接到Internet的计算机上使用MySQL,使用者应该阅读本节以避免常见的安全错误。
在讨论安全性时,有必要充分考虑保护整个服务器主机(不仅仅是MySQL服务器)从而免受所有类型的攻击,例如:窃听、更改、回放和拒绝服务。这里我们没有涵盖可用性和容错相关的介绍。
Mysql针对用户的连接、查询和其他操作采用基于访问控制列表(ACL)的安全策略。还支持MySQL服务端和客户端之间的SSL加密连接。
运行MySQL的时候要遵循以下规则
-
不要让任何人
(root用户除外)访问访问mysql库中的user表。这个很关键。 -
了解MySQL访问权限的工作原理
(请参阅: “Access Control and Account Management”)。使用GRANT和REVOKE语句来控制对 MySQL 的访问。不要授予不必要的权限。永远不要向所有主机授予访问权限。检查清单:
- 尝试运行
mysql -u root。如果你能成功连接到服务器,无需密码,任何人都可以root用户连接到你的MySQL服务器。 - 使用
SHOW GRANTS语句检查哪些帐户可以访问什么。然后使用REVOKE语句删除那些不需要的权限。
- 尝试运行
-
不要在数据库中存储明文密码。如果您的计算机遭到入侵,入侵者可以获取密码的完整列表并使用它们。相反,可以使用
SHA2()或其他一些单向散列函数并存储散列值。 -
投资防火墙。这可以保护您免受至少 50% 的攻击。将 MySQL 置于防火墙之后或隔离区 (
DMZ) 中。尝试使用nmap之类的工具从Internet扫描您的端口。MySQL默认使用3306端口。不受信任的主机不能访问此端口。检查MySQL端口是否打开的简单方法是,在远程机器上尝试以下命令,其中server_host是MySQL服务器运行的主机名或IP地址:shell> telnet server_host 3306 -
访问MySQL的应用程序不应该信任用户输入的任何数据,应该使用适当的防御编程技术编码
-
学习使用
tcpdump和strings工具。在大多数情况下,你可以通过发出如下命令来检查MySQL数据流是否未加密:shell> tcpdump -l -i eth0 -w - src or dst port 3306 | strings
保护密码安全
终端用户密码安全指南
MySQL用户应该使用以下准则来确保密码的安全。
当你运行一个客户端程序去连接MySQL服务的时候,不建议使用可能别其他用户发现的方式制定您的密码。这里列出了在运行客户端程序时用来指定密码的方法,并对每种方法的风险进行了评估。简而言之,最安全的方法是让客户机程序提示输入密码,或者在受适当保护的选项文件中指定密码。
-
使用
mysql_config_editor实用工具,它允许您在名为.mylogin.cnf的加密登录路径文件中存储身份验证凭据。MySQL客户端程序可以读取该文件,以获取连接MySQL服务器的身份验证凭据。请参阅 “mysql_config_editor — MySQL Configuration Utility”. -
在命令行上使用
--password=password或-ppassword选项。例如:shell> mysql -u francis -pfrank db_name -
在未指定密码值的情况下在命令行上使用
--password或-p 选项。在这种情况下,客户端程序以交互方式提交密码:shell> mysql -u francis -p db_name Enter password: ******** -
将您的密码存储在选项文件中。例如,在 Unix 上,您可以在主目录中
.my.cnf文件的[client]部分中列出您的密码:[client] password=password为了确保密码安全,除了您自己之外,任何人都不应访问该文件。为确保这一点,请将文件访问模式设置为
400或600。例如:shell> chmod 600 .my.cnf要从命令行来包含密码的特定选项文件,请使用
--defaults-file=file_name选项,其中file_name是文件的完整路径名。例如:shell> mysql --defaults-file=/home/francis/mysql-opts -
将您的密码存储在
MYSQL_PWD环境变量中。请参见“Environment Variables”.这种指定MySQL密码的方法是极其不安全的,不应该使用
在Unix上,mysql客户端将执行语句的记录写入历史文件(参见4.5.1.3节,“mysql客户端日志”)。默认情况下,该文件名为.mysql_history,创建在您的home目录中。密码可以在CREATE USER和ALTER USER等SQL语句中以纯文本形式写记录,因此如果使用这些语句,它们将被记录在历史文件中。为了保证这个文件的安全,使用限制性的访问模式,方法与前面描述的.my.cnf文件相同。
管理员密码安全指南
数据库管理员应使用以下准则来保证密码的安全。
MySQL 将用户帐户的密码存储在 mysql.user 系统表中。绝不应授予任何非管理帐户访问此表的权限。
validate_password插件可用于对可接受的密码强制执行策略。请参见The Password Validation Plugin。
密码和日志
密码可以在SQL语句中以纯文本形式编写,比如CREATE USER、GRANT、SET PASSWORD,以及调用PASSWORD()函数的语句。如果MySQL服务器按照所写的方式记录这些语句,那么它们中的密码对任何访问日志的人都是可见的。
根据以下语句避免了密码以明文的形式写入语句日志中。
CREATE USER ... IDENTIFIED BY ...
ALTER USER ... IDENTIFIED BY ...
GRANT ... IDENTIFIED BY ...
SET PASSWORD ...
SLAVE START ... PASSWORD = ...
CREATE SERVER ... OPTIONS(... PASSWORD ...)
ALTER SERVER ... OPTIONS(... PASSWORD ...)
这些语句中的密码会被重写从而不会出现在通用的查询日志、慢查询日志和二进制日志的语句文本中。重写不适用其他语句。特别是在mysql.user表上执行insert或者update语句并且该语句使用明文密码,这样的语句会原样的记录语句日志,而不会重写明文密码。所以这是不安全的。所以强烈建议不适用这样的语句。(无论如何,不鼓励直接修改授权表)
对于一般日志,可以通过使用--log-raw选项来组织密码重写。出于安全考虑,不应该在生产环境中使用该选项。
生成的审计日志文件内容不加密。出于安全原因,这个文件应该被写入一个只有 MySQL 服务器和有正当理由查看日志的用户才能访问的目录。请参阅 MySQL Enterprise Audit Security Considerations
如果安装了查询重写插件,服务器接收的语句可能会被重写(请参阅查询重写插件)。在本例中,--log raw选项影响语句日志记录,如下所示:
- 如果没有使用 --log-raw,服务器会记录查询重写插件返回的语句。这可能与收到的声明不同
- 使用
--log-raw,服务器记录收到的原始语句。
使MySQL免受攻击
当你连接MySQL服务的时候,你应该使用密码。密码不会以明文形式在连接上传输。
所有其他信息都以文本的形式传输,任何能够看到连接的人都可以阅读。如果客户机和服务器之间的连接通过一个不受信任的网络,并且您担心这一点,那么可以使用压缩协议使流量更难破译。您还可以使用 MySQL 内部的 SSL 使连接更加安全,请查看Using Encrypted Connections。或者,使用 SSH 在 MySQL 服务器和 客户端之间获得加密的 TCP/IP 连接。
为了使 MySQL 系统更加安全,强烈您考虑以下建议:
-
要求所有的MySQL账户必须有密码。
-
要确保在数据库目录下只有具有读或者写权限的
Unix用户账户才可以运行mysqld。 -
不要以Unix
root用户运行MySQL服务器。这是非常危险的,因为任何具有FILE权限的用户都能够导致服务器以root身份创建文件(例如~root/.bashrc)。为了防止这种情况,mysqld拒绝以root身份运行,除非使用——user=root选项明确指定。mysqld可以(并且应该)以普通的非特权用户身份运行。您可以创建一个名为mysql的单独 Unix 帐户,以使一切更加安全。此帐户仅用于管理 MySQL。要以不同的Unix用户启动mysqld,请在my.cnf选项文件的[mysqld]组中添加一个指定用户名。例如:[mysqld] user=mysql无论是手动启动,还是使用
mysqld_safe或者mysql.server启动MySQL服务器只能由指定的mysql用户来启动MySQL。以
root以外的 Unix 用户身份运行mysqld并不意味着您需要更改user表中的root用户名。 MySQL 帐户的用户名与 Unix 帐户的用户名无关。 -
不要将
FILE权限授予非管理用户。任何拥有此权限的用户都可以使用mysqld守护程序的权限在文件系统中的任何位置写入文件。这包括服务器的数据目录,其中包含实现权限表的文件。为了是FILE权限操作更加安全,可以使用SELECT ... INTO OUTFILE来生成文件而不覆盖已存在的文件,并且任何人都可以写这个文件。FILE权限也能用来读取任何文件。有了这个权限,您可以将任何文件加载到数据库表中。例如可以通过load将/etc/passwd加载到指定表中,然后利用select语句查询该表。要限制读写文件的位置,可以设置
secure_file_priv设置成指定的目录。请参考Server System Variables -
加密二进制文件和中继日志文件。加密有助于保护这些文件和其中包含的潜在敏感数据不被外部攻击者滥用,也防止存储这些文件的操作系统用户未经授权查看。通过将
binlog_encryption系统变量设置为on,就可以在MySQL服务器上启用日志加密。有关更多信息,请参见Encrypting Binary Log Files and Relay Log Files。 -
不要将
PROCESS或SUPER权限授予非管理用户。mysqladmin processlist和SHOW PROCESSLIST的输出显示当前正在执行的任何语句文本,因此任何被允许查看服务器进程列表的用户都可以看到其他用户发出的语句。mysqld为拥有CONNECTION_ADMIN或SUPER权限的用户保留了一个额外的连接,这样即使所有正常连接都在使用中,MySQLroot用户也可以登录并检查服务器活动。SUPER权限可用于终止客户端连接、通过更改系统变量的值更改服务器操作以及控制副本服务器。 -
应使用
“Stored Object Access Control中讨论的安全指南来编写存储程序和视图。 -
如果您不信任您的DNS,您应该在
grant表中使用IP地址而不是主机名。在任何情况下,使用包含通配符的主机名值来创建grant表条目时都应该非常小心。 -
如果希望限制单个帐户允许的连接数,可以通过在
mysqld中设置max_user_connections变量来实现。CREATE USER和ALTER USER语句还支持资源控制选项,用于限制一个帐户允许的服务器使用程度。请参见CREATE USER Statement和ALTER USER Statement。
mysqld安全相关的选项和变量
下表显示了影响安全性的 mysqld 选项和系统变量
| Name | Cmd-Line | Option File | System Var | Status Var | Var Scope | Dynamic |
|---|---|---|---|---|---|---|
| allow-suspicious-udfs | Yes | Yes | ||||
| automatic_sp_privileges | Yes | Yes | Yes | Global | Yes | |
| chroot | Yes | Yes | ||||
| local_infile | Yes | Yes | Yes | Global | Yes | |
| safe-user-create | Yes | Yes | ||||
| secure_file_priv | Yes | Yes | Yes | Global | No | |
| skip-grant-tables | Yes | Yes | ||||
| skip_name_resolve | Yes | Yes | Yes | Global | No | |
| skip_networking | Yes | Yes | Yes | Global | No | |
| skip_show_database | Yes | Yes | Yes | Global | No |
如何以普通用户身份运行 MySQL
在Windows上,你可以使用Windows普通用户来运行MySQL服务来作为Windows的服务。
在Linux上,对于使用MySQL存储库或RPM包执行的安装,MySQL服务器mysqld应该由本地mysql操作系统用户启动。MySQL存储库中包含的init脚本不支持其他操作系统用户启动MySQL。
在Unix(或使用tar.gz包执行安装 )上,MySQL服务器mysqld可以由任何用户启动和运行。但是,出于安全考虑,您应该避免以Unix root用户运行MySQL服务。想要利用一个普通非权限的Unix用户user_name来启动MySQL。你需要做一下事情:
-
使用
mysqladmin shutdown停止MySQL服务。 -
更改数据库目录和文件,以便
user_name有权读取和写入其中的文件(您可能需要以 Unixroot用户身份执行此操作)。shell> chown -R user_name /path/to/mysql/datadir如果不这样做,服务器在以
user_name身份运行时将无法访问数据库或表。 -
以用户
user_name启动服务器。另一种方法是以Unixroot用户的身份使用——user=user_name选项启动mysqld。 -
要在系统启动时自动以给定用户身份启动服务器,请通过在服务器数据中的
/etc/my.cnf选项文件或my.cnf选项文件的[mysqld]组中添加用户选项来指定用户名。例如:[mysqld] user=user_name
LOAD DATA LOCAL 的安全注意事项
LOAD DATA语句将数据文件加载到表中。该语句可以加载服务器主机上的文件,如果指定了LOCAL关键字,则可以加载客户端主机上的文件。
LOAD DATA 的 LOCAL 版本有两个潜在的安全问题:
- 因为
LOAD DATA LOCAL是一个SQL语句,语句的解析发生在服务端,并且文件从客户端主机到服务器主机的传输是由 MySQL 服务器发起的,他通过语句中的文件名告诉客户端想要的文件。打补丁的服务器可以告诉客户端程序需要传输的文件而不是语句中指定的文件。这样服务器可以访问客户端用户具有读取权限的客户端主机上的任何文件。(事实上,打补丁的服务器可以用文件传输请求响应给客户端任何语句,而不仅仅是LOAD DATA LOCAL,所以一个更基本的问题是客户机不应该连接到不受信任的服务器上。) - 在客户端从Web服务器连接的Web环境中,用户可以使用
LOAD DATA LOCAL来读取Web服务器进程中具有读取权限的任何文件(假设用户可以对SQL服务运行任何语句)。在这种环境中,MySQL服务器的客户机实际上是Web服务器,而不是连接到Web服务器的远程程序。
为避免连接到不受信任的服务器,客户端可以通过使用 --ssl-mode=VERIFY_IDENTITY 选项和适当的 CA 证书进行连接,以此建立安全连接并验证服务器身份。
为避免 LOAD DATA 问题,除非采取了适当的客户端预防措施,否则客户端应避免使用 LOCAL。
为了控制本地数据加载,MySQL 允许启用或禁用该功能。此外,从 MySQL 8.0.21 开始,MySQL 允许客户端将本地数据加载操作限制在指定的目录中。
启用或禁用本地数据加载功能
管理员和应用程序可以配置是否允许本地数据加载,如下所示:
- 在服务器端:
local_infile系统变量控制服务器端LOCAL功能。根据local_infile设置,服务器拒绝或允许请求加载客户端本地数据。- 默认情况下,
local_infile是禁用的。要显式地设置服务器拒绝或允许LOAD DATA LOCAL语句,可以启动mysqld时禁用或启用local_infile。Local_infile也可以在运行时设置。
- 在客户端:
ENABLED_LOCAL_INFILECMake 选项控制 MySQL 客户端库的默认编译LOCAL功能- 默认情况下,MySQL二进制发行版中的客户端库在编译时禁用了
ENABLED_LOCAL_INFILE。如果您从源代码编译MySQL,那么根据没有明确安排的客户端是否应该禁用或启用LOCAL功能来禁用或启用ENABLED_LOCAL_INFILE。
限制允许本地数据加载的文件
MySQL Shell 和本地数据加载
客户端编程安全指南
客户端应用程序访问MySQL应该使用以下准则来避免错误地解释外部数据或暴露敏感信息。
- 正确处理外部数据
- 正确处理MySQL错误信息
正确处理外部数据
访问 MySQL 的应用程序不应信任用户输入的任何数据,他们可以通过在 Web 表单、URL 或您构建的任何应用程序中输入特殊或转义字符序列来试图欺骗您的代码。如果用户试图通过输入以下内容来执行SQL注入,DROP DATABASE mysql;请确保应用程序保持安全。这是一个极端的例子,但是如果你没有做好准备,黑客使用类似的技术可能会导致大量的安全漏洞和数据丢失。
一个常见的错误就是只保护字符串数据值。记住也要检查数字数据。如果应用程序在用户输入值234时生成一个查询,如SELECT*FROM table WHERE ID=234,则用户可以输入值234 ro 1=1,使应用程序生成查询SELECT*FROM table WHERE ID=234 ro 1=1。结果检索到了表中的所有数据。这会暴露每一行并导致服务器负载过大。防止此类攻击的最简单方法是在数字常量周围使用单引号:SELECT*from table WHERE ID='234'。如果用户输入额外的信息,这些信息都将成为字符串的一部分。在数字上下文中,MySQL会自动将该字符串转换为数字,并从中删除任何尾随的非数字字符。
有时人们认为,如果数据库只包含可用的公开数据,则不需要保护它。这是不正确的。即使允许在数据库中显示任何行,您仍然应该保护其免受拒绝服务的攻击。否则,服务器对合法用户无响应。
检查清单:
- 启用严格的SQL模式来告诉服务器对它所接受的数据值进行更严格的限制。SQL模式详细信息请查看 Server SQL Modes。
- 尝试在所有的的Web表单中输入单引号和双引号。如果你获取到任何种类的MySQL错误,马上排查这个错误。
- 尝试在URLs上添加
%22 ("), %23 (#), and %27 (') - 尝试使用前面示例中显示的字符将动态 URL 中的数据类型从数字类型修改为字符类型。您的应用程序应该可以安全地抵御这些类似的攻击。
- 尝试在数字字段中输入字符、空格和特殊符号,而不是数字。您的应用程序应该在将它们传递给MySQL之前删除它们或者产生一个错误。将未检查的值传递给MySQL是非常危险的!
- 在将数据传递给MySQL之前检查数据的大小
- 应用程序不应该使用用于管理MySQL的用户名连接MySQL。不要为您的应用程序提供不需要的任何权限。
许多应用程序编程接口提供了转义数据值中的特殊字符的方法。如果正确使用,这将防止应用程序用户输入导致应用程序生成结果与预期不符:
-
MySQL的SQL语句:使用
sql prepared语句并且仅通过占位符接收数据值。了解更多的信息请查看Prepared Statements -
MySQL C API:使用
mysql_real_escape_string_quote()API调用。或者,使用 C APIprepared语句接口并仅通过占位符接受数据值;请参阅C API Prepared Statement Interface.。 -
MySQL++:对查询流使用转义和引号修饰符。
-
PHP:使用
mysqli或pdo_mysql扩展,而不是旧的ext/mysql扩展。首选API支持改进后的MySQL身份验证协议和密码,以及带有占位符的prepared语句。请参见Choosing an API。如果必须使用旧的
ext/mysql扩展,然后为了转移请使用mysql_real_escape_string_quote()函数而不是使用mysql_escape_string()或者addslashes()函数,因为只有mysql_real_escape_string_quote()可以识别字符集,当使用(无效的)多字节字符集时,可以“绕过”其他函数。 -
Perl DBI:使用占位符或
quote()方法。 -
Ruby DBI:使用占位符或
quote()方法。 -
Java JDBC:使用
PreparedStatement对象和占位符。
正确处理MySQL错误信息
应用程序有责任拦截因使用 MySQL 数据库服务器执行 SQL 语句而发生的错误并适当处理它们。
当应用程序从MySQL接收到这样的错误时,一种简单但不安全的方法是截取错误并将其逐字显示给客户端。但是,暴露错误信息是一种已知的应用程序漏洞类型(CWE-209),应用程序开发人员必须确保应用程序不存在此漏洞。
例如如下包含数据名和表名的信息会通过应用程序返回给客户端,从而客户端可能会利用这些信息:
ERROR 1146 (42S02): Table 'mydb.mytable' doesn't exist
相反,当应用程序从MySQL接收到此类错误时,正确的行为是将适当的信息(包括错误信息)记录到一个安全的审计位置,该位置只有可信的人员可以访问。应用程序可以向用户返回一些更通用的信息,比如Internal Error。
1758

被折叠的 条评论
为什么被折叠?



