最近几个月非常忙,所以很少有时间写博客,这几天终于闲了一些,于是就在整理平时的一些笔记。恰好这几天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. Linux下服务器端开发流程及相关工具介绍(C++)

    去年刚毕业来公司后,做为新人,发现很多东西都没有文档,各种工具和地址都是口口相传的,而且很多时候都是不知道有哪些工具可以使用,所以当时就想把自己接触到的这些东西记录下来,为后来者提供参考,相当于一个路 ...

  2. 【Win 10 应用开发】启动远程设备上的应用

    这个功能必须在“红石-1”(build 14393)以上的系统版中才能使用,运行在一台设备上的应用,可以通过URI来启动另一台设备上的应用.激活远程应用需要以下前提: 系统必须是build 14393 ...

  3. Minor【 PHP框架】1.简介

    1.1 Minor是什么 Minor是一个简单但是优秀的符合PSR4的PHP框架,It just did what a framework should do. 只做一个框架应该做的,简单而又强大! ...

  4. vscode 1.5安装体验

    1.下载安装 官方下载地址: http://code.visualstudio.com/ 界面截图: 2.图标显示功能File Icon Themes vscode1.5版本文件夹视图,可显示文件类型 ...

  5. Android系统默认对话框添加图片

    开发工具Android Studio 今天公司UI要求软件对话框改成加图片的,以前没有做过,所以就学习了一下,废话不多说, 看效果: 创建XML文件dialog_lsit_item.xml <L ...

  6. Android之SAX解析XML

    一.SAX解析方法介绍 SAX(Simple API for XML)是一个解析速度快并且占用内存少的XML解析器,非常适合用于Android等移动设备. SAX解析器是一种基于事件的解析器,事件驱动 ...

  7. Ubuntu1604下安装Liggghts及CFDEM Coupling

    部分内容参考http://www.linuxdiyf.com/linux/16315.html LIGGGHTS是一款开源的DEM软件,来自于著名的分子动力学软件LAMMPS,目前借助于CFDEM C ...

  8. 使用Jenkins配置Git+Maven的自动化构建

    Jenkins是一个开源的持续集成工具,应用Jenkins搭建持续集成环境,可以进行自动构建.自动编译和部署,非常方便. 在服务器比较少的情况下,Jenkins的优势并不明显,但是随着项目发展,服务器 ...

  9. css 填坑常用代码分享

    以下是常用的代码收集,没有任何技术含量,只是填坑的积累.转载请注明出处,谢谢. 因为提交比较麻烦,后来转置github:https://github.com/jsfront/src/blob/mast ...

  10. mono for android Listview 里面按钮 view Button click 注册方法 并且传值给其他Activity 主要是context

    需求:为Listview的Item里面的按钮Button添加一个事件,单击按钮时通过事件传值并跳转到新的页面. 环境:mono 效果: 布局代码 主布局 <?xml version=" ...