MySQL安全

1. 安全准则mysql

任何想要在链接到公网的计算机上使用MySQL的人都应该阅读本节,以免最多见的安全错误。算法

在讨论安全性时,有必要考虑彻底保护整个服务器主机,而不只仅是MySQL服务器,以防止全部类型的攻击:窃听,更改,重放和拒绝服务。咱们并不涵盖可用性和容错的全部方面。sql


MySQL使用基于访问控制列表(ACL)的安全策略管理全部的链接、查询以及用户尝试执行的其它操做。MySQL还支持客户端和服务器之间的SSL加密链接。这里讨论的许多概念并不只仅是针对MySQL的,而是几乎适用于全部应用程序的。数据库


运行MySQL时,请遵循如下准则:编程

● 不要给除了root帐号之外的任何人访问mysql.user表的权限!这是相当重要的。安全


● 了解MySQL访问权限系统的工做原理。使用grant和revoke语句来控制对MySQL的访问。不要授予比必要更多的权限。决不授予全部主机的访问权限。bash

检查清单:服务器

        ■ 运行mysql -u root. 若是不须要密码便可链接成功,那么任何人均可以以root帐号的身份链接到你的MySQL服务器;网络

        ■ 使用show grants语句来检查全部帐号的权限。而后使用revoke语句删除那些没必要要的访问权限;tcp


● 不要在你的数据库中存储明文密码。由于若是你的计算机遭到入侵,那么入侵者能够获取完整的密码列表并使用它们。相反,使用SHA2()或其它单向散列函数并存储散列值。


要防止使用彩虹表进行密码恢复,请勿直接在普通密码上使用这些函数;而是应该选择一些字符串做为salt,并使用hash ( hash ( password ) + salt)的值。


● 不要从字典中选择密码。由于存在破解密码的特殊程序。即便像"xfish98"这样的密码也很糟糕。更好的是"duag98",它包含的也是单词"fish",但在标准键盘上取了"fish"中每一个字母左侧的那个字母。另外一种方法是使用句子中每一个单词的首字母提取的密码(例如,"Four score and seven years ago"的密码是"Fsasya")。这样的密码很容易记住和键入,但对于不知道句子的人却很难猜出。并且,你还能够用数字取代数字单词,以得到短语"4 score and 7 years ago",从而产生密码"4sa7ya",这更难以猜想。


● 投资于防火墙。这能够保护你免受来自各类软件全部类型漏洞中至少50%的伤害。将MySQL放在防火墙后面或放在非军事区域(DMZ)中。

检查清单:

        ■ 尝试使用nmap等工具从Internet上扫描你的端口。MySQL的默认端口为3306。这个端口不该该可以被不受信任的主机所访问,即不受信任的主机应不能访问此端口。检查你的MySQL端口是否打开的一个简单方法是,尝试从一些远程计算机上执行如下命令,其中server_host是运行MySQL服务器的主机的主机名或IP地址

若是telnet挂起或链接被拒绝,那么说明端口被限制了,这就是指望的结果。若是链接成功并收到一些垃圾字符,则说明端口是打开的,那么此时你应该在防火墙或路由器上关闭该端口,除非你真的有一个很好的理由保持打开。


● 访问MySQL的应用程序不该该信任用户输入的任何数据,应该使用适当的防护性编程技术编写这些应用程序。


● 不要在互联网上传输普通的(未加密的)数据。全部有时间和能力拦截它的人均可能获取到这些信息,并将其用于本身的目的。相反,咱们应该使用加密的协议,如SSL或SSH。MySQL支持内部SSL链接。另外一种技术是使用SSH端口转发来建立用于通讯的加密(和压缩)隧道。


● 学习使用tcpdump和strings实用工具。在大多数状况下,你能够经过如下命令来检查MySQL数据流是否未加密:


若是你没有看到明文数据,这并不老是意味着信息是加密的。若是你须要高安全性,请咨询安全专家。



2. 保证密码安全

密码出如今MySQL内的几个上下文中。下面的部分提供了指导原则,使终端用户和管理员可以确保这些密码的安全,避免暴露它们。还有一个关于MySQL如何在内部使用密码散列的讨论。


2.1 终端用户密码安全指南

MySQL用户应该使用如下准则来保证密码安全。


当你运行客户端程序链接到MySQL服务器时,以一种将其暴露给其余用户的方式来指定密码是不明智的。这里列出了在运行客户端程序时能够用来指定密码的方法,以及每种方法的风险评估。简而言之,最安全的方法是让客户端程序提示输入密码,或者在恰当保护的选项文件中指定密码。


      ● 在命令行上使用 -pyour_pass 」或「--password=your_pass」选项。例如:

        

这种方法很方便,但不安全。在某些系统中,系统状态程序能够看到你的密码,好比其余用户能够调用ps,以显示命令行。在初始化序列期间,MySQL客户端一般会用零覆盖命令行中的密码参数。可是,仍然有一段短暂的时间间隔,值是可见的。此外,在某些系统上,这种覆盖策略是无效的,密码对于ps仍旧可见。


若是你的操做环境设置为在终端窗口的标题栏中显示当前命令,那么只要命令正在运行,密码仍然可见,即便命令已经在窗口内容区域中滚动了。


      ● 在命令行上使用「-p」或「--password」选项,而不指定密码值。在这种状况下,客户端程序会以交互的方式请求密码:

*表示输入密码的位置。当你输入密码时,密码不会显示出来。


输入密码比在命令行上指定密码更安全,由于它对其余用户不可见。可是,输入密码的方法仅适用于以交互方式运行的程序。若是你想从非交互运行的脚本中调用客户端,则没法从键盘输入密码。在某些系统上,你甚至可能会发现脚本的第一行被读取并错误地解释为密码。


      ● 将你的密码存储在选项文件中。例如,在Unix上,你能够在主目录中的.my.cnf文件的[client]部分列出密码:

为了保证密码的安全,除了你之外,任何人都不能访问这个文件。为了确保这一点,请将文件访问模式设置为400或600。例如:

要在命令行上指定包含密码的特定选项文件,请使用「--defaults-file=file_name」选项,其中file_name是文件的完整路径名。例如:


      ● 将你的密码存储在「MYSQL_PWD」环境变量中。


这种指定MySQL密码的方法是很是不安全的,不该该被使用。某些版本的ps包含了显示运行进程环境的选项。在某些系统上,若是你设置了「MYSQL_PWD」,那么你的密码就会被暴露给运行ps的任何其余用户。即便在没有这种版本的ps的系统上,假设没有其余用户能够检查进程环境的方法也是不明智的。


在Unix上,MySQL客户端会将执行过的语句写入一个历史文件。默认状况下,此文件名为「.mysql_history」,并在你的主目录中建立。例如create user, grant和set password之类的SQL语句中的密码是以纯文本的形式写入的,所以若是你使用了这些语句,则它们将被记录在历史文件中。为了保证这个文件的安全,请使用与「.my.cnf」文件相同的访问限制模式。


若是你的命令解释器被配置为维护历史记录,则用于保存命令的任何文件都将包含在命令行中输入的MySQL密码。例如,bash使用「~/.bash_history」。任何此类文件都应该具备限制性访问模式。


2.2 管理员密码安全指南

数据库管理员应使用如下准则来保证密码安全。


MySQL在mysql.user表中存储帐号密码。不该向任何非管理帐号授予访问该表的权限。


有权修改插件目录(plugin_dir系统变量的值)或指定插件目录位置的my.cnf文件的用户能够替换插件,并修改插件提供的功能,包括验证插件。


应该保护可能写入密码的日志文件等文件。


2.3 密码和日志

在create user, grant, set password以及调用password()函数的SQL语句中密码以纯文本的形式写入。若是这些语句被MySQL服务器记录,那么有权访问日志的任何人均可以看到密码。这适用于通常查询日志,慢查询日志和二进制日志。


审核日志插件生成的审核日志文件的内容未加密。为了安全起见,这个文件应该被写入一个只有MySQL服务器和有合法理由查看日志的用户才能够访问的目录中。


为了防范日志文件被曝光,请将它们放在只有MySQL服务器和数据库管理员才有权访问的目录中。若是服务器将日志记录到「mysql」库表中,则只将这些表的访问权限授予数据库管理员。


复制slave将master的密码存储在「master.info」文件中。限制此文件只能由数据库管理员访问。


使用受限访问模式来保护包含日志表或包含密码的日志文件的数据库备份。



3. 保护MySQL免受攻击

当链接到MySQL服务器时,你应该使用密码。密码不会在链接上以明文的形式传输。在MySQL4.1.1中,客户端链接序列中的密码处理通过升级,变得很是安全。若是你仍然使用的是4.1.1以前的密码,则加密算法不如新算法那样强大。经过一些努力,聪明的攻击者能够嗅探客户端和服务器之间的流量,从而破解密码。


其它全部信息都以纯文本的形式传输,任何可以查看链接的人均可以阅读。若是客户端和服务器之间的链接经过了一个不受信任的网络,而且你为此感到担心,那么你可使用压缩协议使流量更加难以解密。你还可使用MySQL的内部SSL支持,使链接更加安全。或者,使用SSH在MySQL服务器和MySQL客户端之间创建加密的TCP/IP链接。


要确保MySQL系统安全,你务必仔细考虑如下建议:

● 要求全部MySQL帐号都要有密码。客户端程序不必定知道运行它的人的身份。对于客户端/服务器应用程序,用户能够为客户端程序指定任何用户名,这很常见。例如,若是other_user没有密码,那么任何人均可以经过调用mysql -u other_user db_name以其余人的身份链接到服务器。若是全部的帐号都有密码,那么使用其余用户的帐号进行链接就会变得更加困难。


● 确保惟一对数据库目录具备读或写权限的Unix帐号是用于运行mysqld的帐号。


● 不要以Unix root用户的身份运行MySQL服务器。这是很是危险的,由于这将使得任何具备「file」权限的帐号都可以使MySQL服务器以Unix root用户的身份建立文件(例如,~root/.bashrc文件)。为了防止这种状况,mysqld拒绝以root身份运行,除非使用「--user=root」选项显式指明。


mysqld应该做为一个普通的、非特权用户来运行。你能够建立一个单独的Unix帐号,名为mysql,该帐号专门用于管理MySQL,从而使一切更加安全。要以不一样的Unix用户启动mysqld,请在my.cnf选项文件的组[mysqld]中添加一个用户选项,例如:


不管你是手动仍是使用mysqld_safe或mysql.server启动服务器,该选项都将使得服务器以指定的用户身份启动。


以Unix用户而非root用户运行mysqld,并不意味着你须要修改mysql.user表中的root帐号。MySQL帐号的用户名与Unix帐号的用户名没有任何关系。


● 不要将「file」权限授予非管理用户。具备此权限的任何用户均可以以mysqld进程的有效用户ID的身份在文件系统的任何位置写入文件。这包括服务器的数据目录,其中含有实现权限表的文件。为了使「file」权限操做更安全,select ... into outfile生成的文件不会覆盖现有的文件。


「file」权限也能够用来读取全局可读或者是MySQL服务器(mysqld进程的有效用户ID)具备读权限的全部文件。使用此权限,你能够将任何文件读入数据库表。这可能会被滥用,例如,使用「load data」将/etc/passwd加载到表中,而后使用select语句进行显示。


为了限制能够读取和写入文件的位置,请将系统变量「secure_file_priv」设置为特定的目录。


● 不要将「process」或「super」权限授予非管理用户。「mysqladmin processlist」和「show processlist」的输出显示了当前正在执行的全部语句的文本,因此可以查看服务器进程列表的任何用户均可以看到其余用户发起的语句,如update user set password=password('not_secure');


mysqld为拥有「super」权限的用户保留了一个额外的链接,这样即便全部正常的链接都在使用中,MySQL的root帐号也能够登陆并检查服务器的活动。


「super」权限能够终止客户端链接,经过更改系统变量的值来改变服务器的运行,以及控制服务器的复制。


● 不容许使用到库表的符号连接。若是你以root身份运行mysqld,这一点尤其重要,由于这会致使任何对服务器数据目录具备写权限的人均可以删除系统中的任何文件。可使用--skip-symbolic-links选项禁用此功能。


● 存储过程和视图应该参照第20.6节 "存储过程和视图的访问控制"给出的安全准则来编写。


● 若是你不信任你的DNS,则应在权限表中使用IP地址而不是主机名。不管如何,在使用含有通配符的主机名建立权限表条目时,你都应该很是当心。


● 若是要限制单个帐号的链接数量,能够经过设置系统变量「max_user_connections」来实现。grant语句还支持资源控制选项,以限制帐号容许使用的服务器资源。


● 若是插件目录对于服务器来讲是可写的,那么用户可使用select ... into dumpfile将可执行代码写入目录中的文件。为了防止这种状况,你能够设置「plugin_dir」目录,使其仅对服务器开放读权限,或者将「--secure-file-priv」设置为能够安全地进行select写入的目录。



4. 以普通用户运行MySQL

在Linux上,对于使用MySQL仓库、RPM软件包或Debian软件包执行的安装,MySQL服务器mysqld应以本地用户「mysql」的身份启动。以其余操做系统用户的身份启动服务器不受init脚本的支持,其做为安装的一部分被包含在系统中


在Unix上(或使用tar或tar.gz包执行安装的Linux上),MySQL服务器能够由任何用户启动和运行。可是出于安全考虑,你应该避免以Unix root身份运行服务器。要将mysqld更改成以普通的非特权用户user_name的身份运行,你必须执行如下操做:

① 若是服务器正在运行,请使用「mysqladmin shutdown」中止服务器。

② 更改数据库目录和文件,以便user_name对其具备读权限和写权限(你可能须要使用Unix root用户身份执行此操做):


若是不执行此操做,服务器将没法访问数据库或表,当它以user_name运行时。

若是MySQL数据目录中的目录或文件是符号连接,则chown -R可能不会跟随符号连接。这时,你须要查看这些连接,手动更改它们指向的目录和文件。

③ 以user_name用户身份启动服务器。另外一种方法是以Unix root用户运行mysqld,同时使用「--user=user_name」选项。这样的话,mysqld首先启动,而后在接受任何链接以前切换为user_name用户。

④ 要在系统启动时自动以特定的用户运行MySQL服务器,请在/etc/my.cnf选项文件或数据目录中的my.cnf选项文件的[mysqld]组中添加一个用户选项,例如:



若是你的Unix机器自己不安全,你应该为权限表中的「root」帐号分配密码。不然,在该机器上具备登陆帐号的任何用户均可以使用--user=root选项运行mysql客户端并执行任何操做。不管何时,你都应该主动为MySQL帐号分配密码,特别是当主机上存在其余Unix帐号时,更应如此。


5. load data local的安全问题

「load data」语句能够加载位于服务器主机上的文件;若是指定了「local」关键字,则能够加载位于客户主机上的文件。


「local」版的「load data」有两个潜在的安全问题:

● 从客户主机到服务器主机的文件传输由MySQL服务器发起。理论上,服务器能够命令客户端程序传输服务器指定的任何文件,而不是由客户端程序在「load data」语句中指定的文件。因此,攻击者可使用一个虚假的服务器冒充MySQL,或者直接在MySQL服务器外部添加补丁,以达到访问客户主机文件的目的。这样的服务器能够访问客户主机上客户端用户有权访问的任何文件。更糟糕的是,除了「load data local」,实际上服务器能够对任何的语句响应一个文件传输请求,因此一个更根本的问题是客户端不该该链接到不受信任的服务器。


● 在Web环境中,客户端程序是从Web服务器进行链接的。用户可使用「load data local」读取Web服务器有权访问的任何文件(假设用户能够对SQL Server运行任何语句)。在这种环境中,相对于MySQL服务器,客户端是Web服务器,而不是链接到Web服务器的用户运行的远程程序。


为了不出现「load data」问题,客户端应避免使用「local」。为避免链接到不受信任的服务器,客户端可使用「--ssl-verify-server-cert」选项和相应的CA证书来创建安全的链接并验证服务器身份。


为了使管理员和应用程序可以管理本地数据加载功能,「local」配置以下:

● 在服务器端:

■「local_infile」系统变量控制服务器端的「local」功能。根据「local_infile」设置的不一样,服务器拒绝或容许由已开启「local」的客户端发起的本地数据加载。「local_infile」默认开启。

■ 要显式地使服务器拒绝或容许「load data local」语句(无论客户端程序和库在构建时或运行时如何配置),请在启动mysqld的同时禁用或启用「local_infile」。「local_infile」也能够在运行时设置。

● 在客户端:

■「enabled_local_infile」CMake选项控制MySQL客户端库「local」功能的静态编译。没有显式指定的客户端,根据MySQL构建时指定的「enabled_local_infile」设置,禁用或启用「local」功能。

MySQL二进制发布版中的客户端库在编译时默认启用了「enabled_local_infile」。若是从源码编译MySQL,则根据没有显式指定的客户端是否应该禁用或启用「local」功能,将「enabled_local_infile」配置为禁用或启用。

■ 使用C API的客户端程序能够经过调用mysql_options()禁用或启用「mysql_opt_local_infile」选项,显式地控制数据加载。

■ 对于mysql客户端,默认禁用本地数据加载。要显式地禁用或启用它,请使用「--local-infile=0」或「--local-infile[=1]」选项。

■ 对于mysqlimport客户端,默认禁用本地数据加载。要显式地禁用或启用它,请使用「--local=0」或「--local=[1]」选项。

■ 若是你是在Perl脚本或读取选项文件[client]组的其它程序中使用「load data local」,则能够向该组添加「local-infile」选项。为了防止不理解该选项的程序出现问题,请使用「loose-」前缀:



在全部状况中,客户端成功使用「ocal」加载操做也要求服务器容许它。


若是服务器或客户端禁用了「local」功能,那么尝试发起「load data local」语句的客户端将收到如下错误消息:



6. 客户端编程安全指南

访问MySQL的应用程序不该该信任用户输入的任何数据,他们能够经过在Web表单,URL或你构建的任何应用程序中输入特殊的或转义的字符序列来尝试欺骗你的代码。确保你的应用程序在用户输入相似「; drop database mysql;」的语句时仍然是安全的。这是一个极端的例子,但若是你不为此作准备,黑客使用相似技术将引起极大的安全漏洞和数据丢失。


一个常见的错误是只保护字符串数据值。记得要检查数值型数据。若是应用程序在用户输入「234」时生成诸如「select * from table where id = 234」的查询,则用户能够输入「234 or 1 = 1」,使应用程序生成查询「select * from table where id = 234 or 1 = 1」。结果,服务器检索表中的每一行。这会暴露全部数据行,并致使服务器负载过大。防止此类攻击的最简单的方法是在数值常量周围使用单引号:select * from table where id = '234'。若是用户输入额外的信息,它们都将称为字符串的一部分。在数值上下文中,MySQL会自动将此字符串转换为数字,并自动剥离末尾的非数字字符,相似于「atoi」或「atof」函数。


有时人们认为,若是一个数据库只包含公开可用的数据,那么它就不须要被保护。这是不正确的。即便容许显示数据库中的任何数据行,你仍然应该防止拒绝服务攻击(例如,那些基于上一段中的技术致使服务器资源浪费的攻击)。不然,你的服务器就没法对合法用户做出响应。


检查清单:

● 启用严格的SQL模式,以告知服务器对其接受的数据值进行更严格的限制。参见第5.1.8节 "服务器SQL模式"。

● 尝试在全部Web表单中输入单引号和双引号。若是你遇到任何类型的MySQL错误,请当即调查问题。

● 尝试经过向其中添加%22("),%23(#)和%27(')来修改动态URL。

● 尝试使用前面示例中所示的字符,将动态URL中的数据类型从数值类型修改成字符类型。你的应用程序应该对这些和相似的攻击是安全的。

● 尝试在数值字段中输入字符、空格和特殊符号,而不是数字。你的应用程序应该在将字段值传递给MySQL以前删除这些非法字符,或者直接生成一条错误提示。将未经检查的值传递给MySQL是很是危险的!

● 在将数据传递给MySQL以前检查数据的大小。

● 应用程序链接数据库的帐号不该是管理帐号。不要给你的应用程序任何不须要的访问权限。

许多应用程序编程接口提供了转义数据值中特殊字符的方法。若是使用得当,这将防止应用程序用户输入致使应用程序生成与你打算的效果不一样的语句的值:

● MySQL C API:使用mysql_real_escape_string() API调用。

● MySQL++:对查询流使用escape和quote修饰符。

其它编程接口可能具备相似的功能。

相关文章
相关标签/搜索