首页 > 代码库 > Redis研究(十三)—安全和通信协议

Redis研究(十三)—安全和通信协议

一、安全

      Redis的作者Salvatore Sanfilippo曾经发表过Redis宣言,其中提到Redis以简洁为美。同样在安全层面Redis也没有做太多的工作。

1、可信的环境
      Redis的安全设计是在“Redis运行在可信环境”这个前提下做出的,在生产环境运行时不能允许外界直接连接到Redis服务器上,而应该通过应用程序进行中转,运行在可信的环境中是保证Redis安全的最重要方法。

      Redis的默认配置会接受来自任何地址发送来的请求,即在任何一个拥有公网IP的服务器上启动Redis服务器,都可以被外界直接访问到。要更改这一设置,在配置文件中修改bind参数,如只允许本机应用连接Redis,可以将bind参数改成:

[html] view plaincopyprint?
  1. bind 127.0.0.1  

bind参数只能绑定一个地址。如果想更自由地设置访问规则需要通过防火墙来完成。
注释:Redis可能会在2.8版本中支持绑定多个地址,参见https://github.com/antirez/redis/issues/274。


2、数据库密码
       除此之外,还可以通过配置文件中的requirepass参数为Redis设置一个密码。例如:

[html] view plaincopyprint?
  1. requirepass TAFK(@~!ji^XALQ(sYh5xIwTn5Ds7JF  

客户端每次连接到Redis时都需要发送密码,否则Redis会拒绝执行客户端发来的命令。例如:

[html] view plaincopyprint?
  1. redis>GET foo  
  2. (error)ERR operation not permitted  


发送密码需要使用AUTH命令,就像这样:

[html] view plaincopyprint?
  1. AUTH TAFK(@~!ji^XALQ(sYh5xIwTn5Ds7JF  
  2. OK  


之后就可以执行任何命令了:

[html] view plaincopyprint?
  1. redis>GET foo  
  2. "1"  


      由于Redis的性能极高,并且输入错误密码后Redis并不会进行主动延迟(考虑到Redis的单线程模型),所以攻击者可以通过穷举法破解Redis的密码(1秒内能够尝试十几万个密码),因此在设置时一定要选择复杂的密码。
      配置Redis复制的时候如果主数据库设置了密码,需要在从数据库的配置文件中通过masterauth 参数设置主数据库的密码,以使从数据库连接主数据库时自动使用AUTH命令认证。


3、命名命令
      Redis支持在配置文件中将命令重命名,比如将FLUSHALL 命令重命名成一个比较复杂的名字,以保证只有自己的应用可以使用该命令。就像这样:

[html] view plaincopyprint?
  1. rename-command  FLUSHALL oyfekmjvmwxq5a9c8usofuo369x0it2k  


如果希望直接禁用某个命令可以将命令重命名成空字符串:

[html] view plaincopyprint?
  1. rename-command  FLUSHALL ""  


注意:无论设置密码还是重命名命令,都需要保证配置文件的安全性,否则就没有任何意义了。


二、 通信协议

      Redis通信协议是Redis客户端与Redis之间交流的语言,通信协议规定了命令和返回值的格式。了解Redis通信协议后不仅可以理解AOF文件的格式和主从复制时主数据库向从数据库发送的内容等,还可以开发自己的Redis客户端(不过由于几乎所有常用的语言都有相应的Redis客户端,需要使用通信协议直接和Redis打交道的机会确实不多)。

      Redis支持两种通信协议,一种是二进制安全的统一请求协议(unified request  protocol),一种是比较直观的便于在telnet程序中输入的简单协议。这两种协议只是命令的格式有区别,命令返回值的格式是一样的。

1、简单协议
      简单协议适合在telnet程序中和Redis通信。简单协议的命令格式就是将命令和各个参数使用空格分隔开,如“EXISTS foo”、“SET foo bar”等。由于Redis解析简单协议时只是简单地以空格分隔参数,所以无法输入二进制字符。我们可以通过telnet程序测试:

[html] view plaincopyprint?
  1. telnet 127.0.0.1 6379  
  2. Trying 127.0.0.1...  
  3. Connected  to localhost.  
  4. Escape character is ‘^]‘ .  
  5. SET foo bar  
  6. +OK  
  7. GET foo  
  8. $3  
  9. bar  
  10. LPUSH plist 1 2 3  
  11. :3  
  12. LRANGE plist 0 -1  
  13. *3  
  14. $1  
  15. 3  
  16. $1  
  17. 2  
  18. $1  
  19. 1  
  20. ERRORCOMMAND  
  21. -ERR unknown command  ‘ERRORCOMMAND‘  


     Redis 2.4之前的版本对于某些命令可以使用类似简单协议的特殊方式输入二进制安全的参数,例如:

[html] view plaincopyprint?
  1. C:SET foo 3  
  2. C:bar  
  3. S:+OK  

其中C:表示客户端发出的内容,S:表示服务端发出的内容。第一行的最后一个参数表示字符串的长度,第二行是字符串的实际内容,因为指定了长度,所以第二行的字符串可以包含二进制字符。但是这个协议已经废弃,被新的统一请求协议取代。“统一”二字指所有的命令使用同样的请求方式而不再为某些命令使用特殊方式,如果需要在参数中包含二进制字符应该使用统一请求协议。


     我们在telnet程序中输入的5条命令恰好展示了Redis的5种返回值类型的格式,之前展现形式是经过了redis-cli封装的,而上面的内容才是Redis真正返回的格式。下面分别介绍。


(1)错误回复
错误回复(error reply )以-开头,并在后面跟上错误信息,最后以\r\n 结尾:

[html] view plaincopyprint?
  1. -ERR unknown command ‘ERRORCOMMAND‘\r\n  


(2)状态回复
状态回复(status reply )以+开头,并在后面跟上状态信息,最后以\r\n 结尾:

[html] view plaincopyprint?
  1. +OK\r\n  


(3)整数回复
整数回复(integer reply )以:开头,并在后面跟上数字,最后以\r\n 结尾:

[html] view plaincopyprint?
  1. :3\r\n  


(4)字符串回复
字符串回复(bulk  reply )以$开头,并在后面跟上字符串的长度,并以\r\n 分隔,接着是字符串的内容和\r\n :

[html] view plaincopyprint?
  1. $3\r\nbar\r\n  

如果返回值是空结果nil,则会返回$-1以和空字符串相区别。


(5)多行字符串回复
多行字符串回复(multi-bulk  reply )以*开头,并在后面跟上字符串回复的组数,并以\r\n 分隔。接着后面跟的就是字符串回复的具体内容了:

[html] view plaincopyprint?
  1. *3\r\n1\r\n3\r\n1\r\n2\r\n1\r\n1\r\n  



2、 统一请求协议
      统一请求协议是从Redis 1.2开始加入的,其命令格式和多行字符串回复的格式很类似,如SET foo bar的统一请求协议写法是“*3\r\n3\r\nSET\r\n3\r\nfoo\r\n3\r\nbar\r\n ”。还是使用telnet进行演示:

[html] view plaincopyprint?
    1. telnet 127.0.0.1 6379  
    2. Trying 127.0.0.1...  
    3. Connected to localhost.  
    4. Escape character is ‘^]‘ .  
    5. *3  
    6. $3  
    7. SET  
    8. $3  
    9. foo  
    10. $3  
    11. bar  
    12. +OK 
    13. http://www.youyuanapp.com/thread-11419-1-1.html
      http://www.youyuanapp.com/thread-11418-1-1.html
      http://www.youyuanapp.com/thread-11417-1-1.html
      http://www.youyuanapp.com/thread-11412-1-1.html
      http://www.youyuanapp.com/thread-11409-1-1.html
      http://www.youyuanapp.com/thread-11404-1-1.html
      http://www.youyuanapp.com/thread-11403-1-1.html
      http://www.youyuanapp.com/thread-11398-1-1.html
      http://www.youyuanapp.com/thread-11397-1-1.html
      http://www.youyuanapp.com/thread-11395-1-1.html
      http://yishujiayuanq.blog.163.com/blog/static/2447250612015025101147963/
      http://yishujiayuanq.blog.163.com/blog/static/2447250612015025101251829/
      http://yishujiayuanq.blog.163.com/blog/static/244725061201502510133740/
      http://yishujiayuanq.blog.163.com/blog/static/2447250612015025101653328/
      http://yishujiayuanq.blog.163.com/blog/static/2447250612015025101718995/
      http://yishujiayuanq.blog.163.com/blog/static/2447250612015025101738627/
      http://yishujiayuanq.blog.163.com/blog/static/2447250612015025101822599/
      http://yishujiayuanq.blog.163.com/blog/static/2447250612015025101841318/
      http://yishujiayuanq.blog.163.com/blog/static/2447250612015025101927982/
      http://yishujiayuanq.blog.163.com/blog/static/244725061201502510197287/

Redis研究(十三)—安全和通信协议