记一次Redis被攻击的事件

 

最近几个月非常忙,所以很少有时间写博客,这几天终于闲了一些,于是就在整理平时的一些笔记。恰好这几天Redis服务器发生了问题,就记录一下。

我司有两款分别是2B和2C的App,类似于阿里旺旺的卖家版和买家版,里面有一个聊天的功能模块。双方可以通过这个功能聊天。内部通讯使用了环信,只是将本地账号和环信账号进行了关联。其他的信息,比如用户基本信息,好友关系,群组关系等存在Redis中,为防止Redis出现问题导致数据丢失(尽管配置了持久化),同时使用消息队列将数据写入SQLServer中进行了冗余。这是一种Redis的典型使用场景,从速度和效率上满足要求。

线上环境一直运行正常,但是在上周日(一个本该休息的日子),领导打电话过来说线上环境的用户登录不了,无法聊天,没有群相关信息。我想估计是Redis出现了问题,让领导不要着急,先让运维看看服务是否还在运行,不行的话,把Redis重启一下,因为之前设置过持久化,数据应该不会丢失。于是继续陪夫人吃饭,看电影。

到了晚上,还是打电话过来说有问题,没办法只有来公司一趟。打开机器用RedisClient连Linux环境上的Redis,发现里面的数据全没了,只有几个新注册的孤零零的用户在里面,老用户全没了,我当时惊呆了,以为是运维那边没搞好,是重启的整个服务器而不是重启的Redis,可能是Redis没有及时保存把数据给弄丢了。看到现象之后跟领导电话告知目前的现象和建议的解决方案,在授权下,重新把用户相关数据从SQLServer同步到了Redis中,关键的数据还好没丢失,然后让测试简单测试了一下,一切正常就没有太在意。而且由于Redis是安装在Linux上的,是由运维同事维护的,出问题了我这边也查不了,于是就回去了。

但是事情远没有那么简单,星期一的时候,领导又打电话过来说聊天又不能用了,比较急说要赶紧处理,我说好,于是不紧不慢的收拾好出门去公司。心里非常不爽,前段时间加班太猛,周一周二全公司开发都调休放假的。就我一个开发的来到公司之后,打开远程又发现Redis里面的数据全没了。于是又从SQLServer把数据同步到了Redis里,然后检查代码,把除了和聊天相关之外的其他逻辑,比如Redis定时同步服务相关的可能会影响到的地方都暂停了。因为前段时间做了一次重构,担心是代码导致数据丢失,搞完了之后就回去了。

然而好景不长,消停了一天没出问题。今天早上过来上班,领导走过来语重心长的说,聊天又登不上了,上去一看,Redis里面的数据又没了。正好运维的同事也在,也就一起找下存在的漏洞和可能的原因。

原因

前几天无意在微博上看到了乌云平台发的一条漏洞信息

然后意识到是不是Redis没有设置密码访问,导致产生了这个漏洞被利用和攻击了。于是自查,发现了如下内容:

里边多了一个名为crackit的字符串,并且db0这个库是没有使用了的,现有的数据和逻辑都在db1上,刚打开的时候,db1是空的,上图是我重新同步过数据之后的结构。

对比乌云报的漏洞的最后一部分:

这简直就是一模一样啊。并且运维那边发现redis的持久化文件被写到了authorized_keys文件中:

确认被攻击之后,于是开始着手修复。之前在开发的时候,其实是有考虑给Redis加访问密码的,不知道后来太忙了,竟然给忘了,也想着公司比较小,应该不会被黑之类的,没想到这次就撞上了。

解决方法

解决方法当然是给Redis加密码,然后在访问的时候,设置密码访问。

最初,访问Redis的客户端我们使用的是ServiceStack.Redis,我之前也写过几篇Redis相关的文章。 由于考虑到授权的原因,使用的是2.0版本的dll,在其API中,从签名看,没有地方可以设置密码:

这个地方只能设置主机名称和端口号。

于是在使用中,比如下面这个删除群组的操作,RedisHelper类是对ServiceStack.Redis的一个封装类,只需要设置主机和端口号:

public static bool DeleteGroup(string groupId)
{
lock (lockDeleteGroup)
{
bool result = true;
using (RedisHelper redis = new RedisHelper(HOST, PORT))
{
var group = redis.GetWhereObj<GroupTable>(GROUPTABLE, x => x.GroupId == groupId);
group.Is_Deleted = true;
result = redis.Update<GroupTable>(GROUPTABLE, x => x.GroupId == groupId, group);
}
return result;
}
}

后面为了安全考虑,需要给Redis设置个密码,于是下载了最新版本的4.0的ServiceStack.Redis,这个是商业化版本的,使用中发现,是有限制的,在其下载页面最下角也有说明:

每小时只能请求6000次,这显然不能满足要求。除了以上限制之外,在使用过程中也出现过一些相当诡异的问题,比如通过Id查找的时候,其只能在1k条范围内进行查询等等,当然,也有可能是因为使用不正确,考虑到以上原因,加之之前的数据组织和结构设计不合理,于是决定重构。

重构的时候,就直接换了另一个C#客户端,StackExchange.Redis

private static ConnectionMultiplexer _redis;
private static IDatabase _db;
private static IServer _server;
private static bool needSave = false;
private void Init(string host, int port, string pwd, int database)
{
var options = ConfigurationOptions.Parse(host + ":" + port);
options.SyncTimeout = int.MaxValue;
options.AllowAdmin = true;
if (!string.IsNullOrEmpty(pwd))
{
options.Password = pwd;
}
if (_redis == null)
_redis = ConnectionMultiplexer.Connect(options);
if (_server == null)
_server = _redis.GetServer(host + ":" + port);
if (_db == null)
_db = _redis.GetDatabase(database);
needSave = false;
}

这里面,可以直接对options对象设置Password属性。于是对该对象进行了包装,后面使用Redis可以这样,在构造函数里边传入PWD即可,比如下面判断用户是否存在的接口:

public static bool HasShopUser(string userName)
{
bool hasUser = false;
ShopUserEntity userEntity;
userEntity = null;
using (RedisHelper redis = new RedisHelper(HOST, PORT, PWD))
{
userEntity = redis.GetShopUserInfo(userName);
} if (userEntity != null)
{
hasUser = true;
}
return hasUser;
}

在替换Redis客户端访问类的时候,顺便对之前Redis里面的数据结构进行了一次重构,经过这次重构,速度提升很明显。于是,于是就直接弄到正式环境然后就把设置密码这个事情给忘记了。

当然,部署有Redis的Linux服务器也按照漏洞建议做了登陆限制和修复。

总结

其实这是一个很低级的错误,访问Redis没有设置密码(当然也可能是Redis所在的Linux服务器本身没有对登录做限制),也感谢有乌云这么好的一个平台,能够及时发现系统的问题和漏洞,避免出现更大的损失。作为一个码农其实不应该抱有这样的侥幸心理,就像墨菲定律说的那样“会出错的,终将会出错“ 。最后,希望这篇文章能给大家一个提醒和一些帮助。

Redis被攻击的更多相关文章

  1. 记一次Redis被攻击的事件

    最近几个月非常忙,所以很少有时间写博客,这几天终于闲了一些,于是就在整理平时的一些笔记.恰好这几天Redis服务器发生了问题,就记录一下. 我司有两款分别是2B和2C的App,类似于阿里旺旺的卖家版和 ...

  2. 公司外网测试服务器 redis 被攻击复盘

    最近 公司外网的测试的 redis 服务器被攻击,最开始是用 docker 搭建的 直接裸奔在外网,任何域名都可以通过 ip+6379来访问,最开始想的是测试服务器也没有啥,后面直接就被人登陆进去改了 ...

  3. redis被攻击,怎么预防

    今天,自己的redis服务器被黑客攻击了,数据全部被删除 从图中可以看到,在db0中多了一个crackit,他就是罪魁祸首,他的值就是ssh无密码连接时需要的authorized_keys. 我们被攻 ...

  4. Windows下Redis缓存服务器的使用 .NET StackExchange.Redis Redis Desktop Manager

    Redis缓存服务器是一款key/value数据库,读110000次/s,写81000次/s,因为是内存操作所以速度飞快,常见用法是存用户token.短信验证码等 官网显示Redis本身并没有Wind ...

  5. 关于 Redis 访问安全性的问题

    升级版本 3.0.2 版本升级到 redis-3.2.0 版本远程无法访问,比较配置文件有些变化,比如默认只能本地的机器才能访问 3.0.2 版本 # By default Redis listens ...

  6. Windows下Redis缓存服务器的使用 .NET StackExchange.Redis Redis Desktop Manager 转发非原创

    Windows下Redis缓存服务器的使用 .NET StackExchange.Redis Redis Desktop Manager   Redis缓存服务器是一款key/value数据库,读11 ...

  7. windows下的redis集群搭建的艰辛历程

    我是参照这两篇教程进行的操作:   1.https://www.cnblogs.com/weiqinl/p/6490372.html   (主) 2.https://blog.csdn.net/qiu ...

  8. C#版-Redis缓存服务器在Windows下的使用

    Redis缓存服务器是一款key/value数据库,读110000次/s,写81000次/s,因为是内存操作所以速度飞快,常见用法是存用户token.短信验证码等 官网显示Redis本身并没有Wind ...

  9. [转]Windows下Redis缓存服务器的使用 .NET StackExchange.Redis Redis Desktop Manager

    转自:http://www.cnblogs.com/oppoic/p/6165581.html Redis缓存服务器是一款key/value数据库,读110000次/s,写81000次/s,因为是内存 ...

随机推荐

  1. 在Xshell中上传下载文件到本地(linux中从多次ssh登录的dbserver里面的文件夹)

    在Xshell中上传下载文件到本地(linux中从多次ssh登录的dbserver里面的文件夹) 1 列出所有需要copy的sh文件 -bash-4.1$ ll /mysqllog/osw/*.sh ...

  2. hdu4857 逃生 bestcoder round1 A

    题目要求要求在满足约束条件的情况下,使小的序号尽力靠前. 坑点就在这里.小的序号尽量靠前并非代表字典序,它要求多种情况时,先使1靠前(可能1仅仅能在第2或第3位 那么就要使它在第2位),其次2,3. ...

  3. Android基于cordova3.3插件开发

    最近的工作项目,需要使用cordova插件开发,详细Cordova角色,不会走,你可以去百度自身OK该,直接启动.详细过程,我有一个小Demo解说提前进行. 还只是接触,东西太理论基础,我也不太清楚, ...

  4. XML解析中的namespace初探

    原文:XML解析中的namespace初探 初学者在解析XML文件的时候最容易遇到的问题恐怕就是XML的namespace了,本文旨在对namespace做一个简要的介绍. namespace的意义无 ...

  5. Nginx安装手冊以及图片server部署

    1.      安装gcc yum install gcc 2.      安装pcre,pcre-devel 在zhoulh文件夹下建立source build文件夹 mkdir source bu ...

  6. 使用zzip和minizip解压缩文件

    #include <zzip/zzip.h> #include <zlib.h> #include <zip.h> #include <unzip.h> ...

  7. 【从翻译mos文章】Weblogic AdminServer 启动fail,报错&quot;unable to get file lock, will retry&quot;

    Weblogic AdminServer 启动fail,报错"unable to get file lock, will retry" 参考原始: Weblogic AdminSe ...

  8. Source Insight 光标变粗设置NotePad++光标设置

    为了更好的查看文档和代码,避免半天都找不到光标的情况,故做此说明 Source Insight 光标变粗 菜单中 Options --->Preferences --->Typing -- ...

  9. response.setHeader各种使用方法

    一秒刷新页面一次 response.setHeader("refresh","1"); 二秒跳到其它页面 response.setHeader("re ...

  10. Microsoft Fakes进行单元测试

    使用Microsoft Fakes进行单元测试(1)   一:什么是单元测试 单元测试是对软件进行准确性验证的步骤.单元测试并不进行整个软件功能的测试,仅仅是对于最小工作单元的测试.一般最小工作单元就 ...