最近几个月非常忙,所以很少有时间写博客,这几天终于闲了一些,于是就在整理平时的一些笔记。恰好这几天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被攻击的事件   最近几个月非常忙,所以很少有时间写博客,这几天终于闲了一些,于是就在整理平时的一些笔记.恰好这几天Redis服务器发生了问题,就记录一下. 我司有两款分别是2B和2C ...

  2. 如何利用redis key过期事件实现过期提醒

    https://blog.csdn.net/zhu_tianwei/article/details/80169900 redis自2.8.0之后版本提供Keyspace Notifications功能 ...

  3. SpringBoot实现监听redis key失效事件

    需求: 处理订单过期自动取消,比如下单30分钟未支付自动更改订单状态 解决方案1: 可以利用redis天然的key自动过期机制,下单时将订单id写入redis,过期时间30分钟,30分钟后检查订单状态 ...

  4. Redis实现之事件

    事件 Redis服务器是一个事件驱动程序,服务器需要处理以下两类事情: 文件事件(file event):Redis服务器通过套接字与客户端(或者其他Redis服务器)进行连接,而文件事件就是服务器对 ...

  5. Redis Key过期事件

    解决方案1: 可以利用redis天然的key自动过期机制,下单时将订单id写入redis,过期时间30分钟,30分钟后检查订单状态,如果未支付,则进行处理但是key过期了redis有通知吗?答案是肯定 ...

  6. 记一次 Confluence 被攻击事件

    故事开始 4 月 14 日,星期天,天气不好,呆在家玩 LOL,正 Happy 的时候同事打电话给我,说 Confluence 看文档的时候挂了,报错:502. 一寻思,不就挂了吗,小意思,重启呗,于 ...

  7. 记Windows服务器Redis 6379被攻击 被设置主从模式同步项目数据

    在工作中第一次经历被攻击,我是一个前端,同时复负责维护一个已上线的项目,在最近一段时间小程序与后台经常出现这个报错, 搜了下说我的从机是只读模式,不能写入,问了同事得知这个项目是单机模式,根本不存在从 ...

  8. Redis源码 - 事件管理

    Redis 的事件分类 分类 描述 定时器 线程内定时响应,更新缓存时间.关闭非活动的客户端连接等等 pipe 线程间通信,用于其他线程通知主线程退出aeApiPoll() unixsocket 本地 ...

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

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

随机推荐

  1. 【AR实验室】OpenGL ES绘制相机(OpenGL ES 1.0版本)

    0x00 - 前言 之前做一些移动端的AR应用以及目前看到的一些AR应用,基本上都是这样一个套路:手机背景显示现实场景,然后在该背景上进行图形学绘制.至于图形学绘制时,相机外参的解算使用的是V-SLA ...

  2. 【AutoMapper官方文档】DTO与Domin Model相互转换(上)

    写在前面 AutoMapper目录: [AutoMapper官方文档]DTO与Domin Model相互转换(上) [AutoMapper官方文档]DTO与Domin Model相互转换(中) [Au ...

  3. jQuery学习之路(6)- 简单的表格应用

    ▓▓▓▓▓▓ 大致介绍 在CSS技术之前,网页的布局基本都是依靠表格制作,当有了CSS之后,表格就被很多设计师所抛弃,但是表格也有他的用武之地,比如数据列表,下面以表格中常见的几个应用来加深对jQue ...

  4. Servlet监听器笔记总结

    监听器Listener的概念 监听器的概念很好理解,顾名思义,就是监视目标动作或状态的变化,目标一旦状态发生变化或者有动作,则立马做出反应. Servlet中的也有实现监听器的机制,就是Listene ...

  5. LoadRunner函数百科叒叒叒更新了!

    首先要沉痛通知每周四固定栏目[学霸君]由于小编外派公干,本周暂停. 那么这周就由云层君来顶替了,当然要要说下自己做的内容啦,DuangDuang! <LoadRunner函数百科>更新通知 ...

  6. mongodb

    修改所有的记录: > db.t_express_apply.update({},{$set:{"isStatus" : 0}},{multi:true})WriteResul ...

  7. 在 SharePoint Server 2016 本地环境中设置 OneDrive for Business

    建议补丁 建议在sharepoint2016打上KB3127940补丁,补丁下载地址 https://support.microsoft.com/zh-cn/kb/3127940 当然不打,也可以用O ...

  8. 烂泥:VMWare Workation双网卡配置IP地址

    本文由ilanniweb提供友情赞助,首发于烂泥行天下 想要获得更多的文章,可以关注我的微信ilanniweb 前几天给一个客户做远程项目实施,客户那边的服务器是Windows OS的,我们这边的业务 ...

  9. css样式让input垂直居中

    css样式让input垂直居中 css代码: .div1{ border: 1px solid #CCC; width:1120px; height:40px; margin:auto; displa ...

  10. Xamarin.Android活动的生命周期

    一.前言 用过Android手机的人一定会发现一种现象,当你把一个应用置于后台后,一段时间之后在打开就会发现应用重新打开了,但是之前的相关的数据却没有丢失.可以看出app的“生命”是掌握在系统手上的, ...