12.3、安全的Redis

安全的Redis

2015年11月,全球数万个Redis节点遭受到了攻击,所有数据都会被清除了,只有一个叫crackit的键存在,这个键的值很像一个公钥。

数据丢失对于很多Redis的开发者来说是致命的,经过相关机构的调查发现,被攻击的Redis有以下特点:

  • Redis所在的机器有外网IP

  • Redis以默认端口6379为启动端口,并且是对外网开放的。

  • Redis是以root用户启动的

  • Redis没有设置密码

  • Redis的bind设置为0.0.0.0或者""

攻击者充分利用Redis的dir和dbfilename两个配置可以利用config set动态设置,以及RDB持久化的特性,将自己的公钥写入到目标机器的/root/.ssh/authotriezed_keys文件中,从而实现了对目标机器的攻陷。

假设机器A是攻击者的机器(内网IP:10.10.xx.192),机器B是被攻击者机器(外网IP:123.16.xx.182),上面部署着一个满足上述五个特性的Redis,下面我们来模拟整个攻击过程:

1)首先确认当前(攻击前)机器A不能通过SSH访问机器B,因为没有权限。

2)由于机器B的外网对外开通了Redis的6379端口,所以可以直接连接到Redis上执行flushall操作,注意此事破坏性已经很大了。

3)在机器A生成公钥,并将公钥保存到一个文件my.pub中。

4)将键crackit的值设置为公钥。

5)将Redis的dir设置为/root/.ssh目录,dbfilename设置为authorized_keys,执行save命令生成RDB文件。此时机器B的/roor/.ssh/authorized_keys包含了攻击者的公钥,之后攻击者就可以“为所欲为”了。

6)此时机器A再通过SSH协议访问机器B,发现可以顺利登录,登录后可以观察/root/.ssh/authorized_keys,可以发现它就是RDB文件。

谁也不想自己的Redis以及机器就这样被攻击吧?本节我们来介绍如何让Redis足够安全。

Redis的设计目标是一个在内网运行的轻量级高性能键值服务,因为是在内网运行,所以对于安全方面没有做太多的工作,Redis值提供了简单的密码机制,并且没有做用户权限的相关划分。那么,在日常对于Redis的开发和运维中要注意哪些方面才能让Redis服务不仅能提供高效稳定的服务,还能保证在一个足够安全的网络环境下运行呢?下面将从7个方面进行介绍。

  1. Redis密码机制

    1. 简单的密码机制

      Redis提供了requirepass配置为Redis提供密码功能,如果添加这个配置,客户端就不能通过redis-cli -h {ip} -p {port}来执行命令。例如下面启动一个密码为hello_redis-devops的redis:

      redis-server --requirepass hello_redis_devpos

      此时通过redis-cli执行命令会受到没有权限的提示:

      # redis-cli
      127.0.0.1:6379> ping
      (error) NOAUTH Authentication required.
      

      Redis提供了两种方式访问配置了密码的Redis:

      • redis-cli -a参数。使用redis-cli连接Redis时,添加-a加密码的参数,如果密码正确就可以正常访问Redis了,具体操作如下:
      # redis-cli -h 127.0.0.1 -p 6379 -a hello_redis_devops
      127.0.0.1:6379> ping
      PONG
      
      • auth命令。通过redis-cli连接后,执行auth加密码命令,如果密码正确就可以正常访问访问Redis了,具体操作如下:
      # redis-cli
      127.0.0.1:6379> auth hello_redis-devops
      OK
      127.0.0.1:6379> ping
      PONG
      
    2. 运维建议

      这种密码机制能在一定程度上保护Redis的安全,但是在使用requirepass时候要注意以下几点:

      • 密码要足够复杂(64个字节以上),因为Redis的性能很高,如果密码比较单间,完全是可以在一段时间内通过暴力破解来破译密码。

      • 如果是主从结构的Redis,不要忘记在从节点的配置中加入masterauth(master的密码)配置,否则会造成主从节点同步失效。

      • auth是通过明文进行传输的,所以也不是100%可靠,如果被攻击者劫持也相当危险。

  2. 伪装危险命令

    1. 引入rename-command

      Redis中包含了很多“危险”命令,一旦错误使用或者误操作,后果不堪设想,例如如下命令:

      • keys:如果键值较多,存在阻塞Redis的可能性。

      • flushall/flushdb:数据全部被清除。

      • save:如果键值较多,存在阻塞Redis的可能性。

      • debug:例如debug reload会重启Redis。

      • config:config应该交给管理员使用。

      • shutdown:停止Redis。

      理论上这些命令不应该给普通开发人员使用,那有没有什么好的方法能够防止这些危险命令被随意执行呢?Redis提供了rename-conmmand配置解决这个问题。下面直接用一个例子说明rename-command的作用。例如当前Redis包含了10000个键值对,现使用flushall将全部是数据清除:

      127.0.0.1:6379> flushall
      OK
      ``
      
      例如Redis添加如下配置:
      
      `rename-command flushall flushalltest`
      
      那么执行flushall命令的话,会收到Redis不认识flushall的错误提示,说明我们成功地使用rename-command对flushall命令做了伪装:
      
      

      127.0.0.1:6379> flushall
      (error) ERR unknonwn command 'flushall'

      
      而如果执行flushalltest,那么就可以实现flushall的功能了,这就是rename-command的作用,管理员可以对认为比较危险的命令做rename-command处理。
      
      
    2. 没有免费的午餐

      rename-command虽然对Redis的安全有一定帮助,但是天下并没有免费的午餐。使用了rename-command时可能会带来如下麻烦:

      • 管理员要对自己的客户端进行修改,例如jedis.flushall()操作内部使用的是flushalll命令,如果用rename-command后需要修改为新的命令,有一定的开发和维护成本。

      • rename-command配置不支持config set,所以启动前一定要确定哪些命令需要使用rename-command。

      • 如果AOF和RDB文件包含了rename-command之前的命令,Redis将无法启动,因为此时它识别不了rename-command之前的命令。

      • Redis源码中有一些命令是写死的,rename-command可能造成Redis无法正常工作。例如Sentinel节点在修改配置时直接使用了config命令,如果对config使用rename-command,会造成Redis Sentinel无法正常工作。

    3. 最佳实践

      在使用rename-command的相关配置时,需要注意以下几点:

      • 对于一些危险的命令(例如flushall),不管是内网还是外网,一律使用rename-command配置。

      • 建议第一次配置Redis时,就应该配置rename-command,因为rename-command不支持config set。

      • 如果涉及主从关系,一定要保持主从节点配置一致性,否则存在主从数据不一致的可能性。

  3. 防火墙

    可以使用防火墙限制输入和输出的IP或者IP范围、端口或者端口范围,在比较成熟的公司都会对有waiwangIP的服务器做一些端口的限制,例如只允许80端口对外开放。因为一般来说,开放外网IP的服务器中Web服务器比较多,但通常存储服务器的端口无序对外开放,防火墙是一个限制外网访问Redis的必杀技。

  4. bind

    1. 对于bing的错误认识

      很多开发者在一开始看到bind这个配置是都是这么认为的:指定Redis只接收来自于某个网段IP的客户端请求。

      但事实上bing指定的是Redis和哪个网卡进行绑定,和客户端是什么网段没有关系。ifconfig命令获取网卡信息包含了三个IP地址:

      • 内网地址:10.10.xx.192

      • 外网地址:220.181.xx.123

      • 回环地址:127.0.0.1

      如果当前Redis配置了bind 10.10.xx.192,那么Redis访问只能通过10.10.xx.192这块网卡进入,通过redis-cli -h 220.181.xx.123 -p 6379和本机redis-cli -h 128.0.0.1 -p 6379都无法连接到Redis。只能通过10.10.xx.192作为redis-cli的参数。

      bind参数可以设置多个,例如下面配置表示当前Redis只接收来自10.10.xx.192和127.0.0.1的网络流量:

      bind 10.10.xx.192 127.0.0.1
      

      运维提示:Redis3.0中bind默认值为"",也就是不限制网卡的访问,但是Redis3.2中必须显示的配置bind 0.0.0.0才可以达到这种效果。

    2. 建议

      经过上面的实验以及对于bind的认识,可以得出如下结论:

      • 如果机器有外网IP,但是部署的Redis是给内部使用的,建议去掉外网网卡或者使用bind配置限制流量从外网进入。

      • 如果客户端和Redis部署在一台机器上,可以使用回环地址127.0.0.1。

      • bind配置不支持config set,所以尽可能在第一次启动前配置好。

      如果当前Redis没有配置密码,没有配置bind,那么只允许来自本机的访问,也就是相当于配置了bind 127.0.0.1。

  5. 定期备份数据

    天有不测风云,假如有一天Redis真的被攻击了(清理了数据,关闭了进程),那么定期备份的数据能够在一定程度挽回一些损失,定期备份持久化数据是一个比较好的习惯。

  6. 不使用默认端口

    Redis的默认端口是6379,不使用默认端口从一定程度上可降低被入侵者发现的可能性,因为入侵者通常本身也是一些攻击程序,对目标服务器进行端口扫描,例如MySQL的默认端口是3306、Memcache的默认端口11211、Jetty的默认端口8080等都会被设置成攻击目标,Redis作为一款较为致知名的NoSQL服务,6379必然也在端口的扫毛的列表中,虽然不设置默认端口还是有可能被攻击者入侵,但是能够在一定程度上降低被攻击的概率。

  7. 使用非root用户启动

    root用户作为管理员,权限非常大。如果被入侵者获取root权限后,就可以在这台机器以及相关机器上“为所欲为”了。笔者建议在启动Redis服务的使用使用非root用户启动。事实上许多服务,例如Resin、Jetty、HBase、Hadoop都建议使用非root启动。

你可能感兴趣的:(12.3、安全的Redis)