Redis被入侵之后

作者: 贾亦真亦贾 | 来源:发表于2017-03-17 09:28 被阅读85次

    之前开发的东华车管OBD的数据采集端一直比较稳定,所采用的技术方案是以Netty为网络框架,以Redis作为消息队列和存储工具,进行数据采集、存储和发送,虽然经历了不少问题,但是经过一段时间的处理之后,相对来说还是比较稳定的,最长时间连续运行近一个月没有出现过任何问题,不过最近我们的服务端几乎每天都崩溃,这让我非常的纳闷,已经稳定运行了好几个月的平台从未出现过如此严重的稳定性问题,所以我也非常重视这个Bug,以最快的时间开始着手解决问题。

    Bug的内容是这样的:

    Could not get a resource from the pool
    java.lang.NullPointerException
    

    第一句是说,没办法从jedis连接池里获取jedis对象了,第二句是说,空指针异常了。
    从错误的本身理解,我的第一反应是,坏了,是不是我的Jedis连接池没有正常回收,导致连接池中的连接数被用完了?
    当然我是比较怀疑的,因为:
    1.采集的车辆数据并不多,由于并发造成的连接池连接数被用完的可能性微乎其微。
    2.运行了好几个月了都没有出现这种情况,在车辆没有大批量增加且车辆总数并不多的情况下,怎么可能突然就开始出现这种问题呢?
    但是,Bug is Bug,该解决你就得解决,所以我就立即从连接数回收的方向去入手,看看是不是我的Jedis写的有问题。

    虚惊一场

    通过谷歌,我找到了别人写的Jedis服务类:

    // 生成多机连接信息列表
    List<JedisShardInfo> shards = new ArrayList<JedisShardInfo>();
    shards.add( new JedisShardInfo("127.0.0.1", 6379) );
    shards.add( new JedisShardInfo("192.168.56.102", 6379) );
    
    // 生成连接池配置信息
    JedisPoolConfig config = new JedisPoolConfig();
    config.setMaxIdle(10);
    config.setMaxTotal(30);
    config.setMaxWaitMillis(3*1000);
    
    // 在应用初始化的时候生成连接池
    ShardedJedisPool pool = new ShardedJedisPool(config, shards);
    
    // 在业务操作时,从连接池获取连接
    ShardedJedis client = pool.getResource();
    try {
        // 执行指令
        String result = client.set("key-string", "Hello, Redis!");
        System.out.println( String.format("set指令执行结果:%s", result) );
        String value = client.get("key-string");
        System.out.println( String.format("get指令执行结果:%s", value) );
    } catch (Exception e) {
        // TODO: handle exception
    } finally {
        // 业务操作完成,将连接返回给连接池if (null != client) {
            pool.returnResource(client);
        }
    } // end of try block// 应用关闭时,释放连接池资源
    pool.destroy();
    
    

    我又看了看我的Jedis回收写法:

        public static void close(Jedis jedis) {
            try {
                jedis.close();
            } catch (Exception e) {
                if (jedis.isConnected()) {
                    jedis.quit();
                    jedis.disconnect();
                }
            }
        }
    
    
    

    发现了和人家写的怎么不一样啊?
    这句:

     pool.returnResource(client);
    

    我怎么没有写啊?
    当时大骂自己粗心,回收方法都没写对怎么就能心安理得的发布程序呢?于是赶紧打开IDE加上这句话,可是当我在我的程序中加上上边这句代码的时候,突然发现这行代码是被遗弃的了,旁边写着提示:

    Deprecated. starting from Jedis 3.0 this method will not be exposed. Resource cleanup should be done using @see redis.clients.jedis.Jedis.close()
    意思是Jedis 3.0以后,这句代码已经被jedis.close()方法替代了,所以我的写法是没有问题的,之前是虚惊一场。

    初见端倪

    好不容易缓口气,但是依旧迷茫,既然我的回收写法是正确的,那为什么我的服务突然就无法从连接池获取连接了呢?
    带着一头的雾水,我开始在服务器的Redis服务上找原因。
    当时我怀疑的是,是不是Redis的服务挂了?第一反应去WIndows的服务里去看,结果发现没有挂,正常运行中。
    我想既然没挂,那我连接一下看看吧。
    于是就用redis-cli命令直接连接服务,连上了没问题。
    然后想说 看看缓存队列中还存着哪些数据吧?
    于是就打了一个命令:

    lrange obdMessage 0 -1
    

    结果Redis直接报了警:

    NOAUTH Authentication required
    

    这里突然感觉不太对劲了,怎么好好的报了这个错误呢?而且我的Redis是有密码的,不过很简单是 111111
    我赶紧用命令:

    auth 111111
    

    来访问我的Redis,结果还是报错,直到这里我才发现,可能是我的Redis访问密码被人动了手脚。

    着手解决

    于是我重启了Redis的服务,看看重启后能不能访问Redis,结果还好,重启后Redis是可以访问了,我在查看存储的数据的过程中,突然发现好像多了一个字段:crackit。
    然后我去网上一搜,出现这个Crakit就说明我的Redis肯定是被人入侵了,那么很有可能就就是我的Redis被人入侵之后,黑客改了我的Redis的密码,导致我的Redis无法使用。

    入侵的漏洞:

    Redis 默认情况下,会绑定在 0.0.0.0:6379,这样将会将 Redis 服务暴露到公网上,如果在没有开启认证的情况下,可以导致任意用户在可以访问目标服务器的情况下未授权访问 Redis 以及读取 Redis 的数据。攻击者在未授权访问 Redis 的情况下可以利用 Redis 的相关方法,可以成功在 Redis 服务器上写入公钥,进而可以使用对应私钥直接登录目标服务器。
    入侵的特征:
    Redis 可能执行过 FLUSHALL 方法,整个 Redis 数据库被清空
    在 Redis 数据库中新建了一个名为 crackit(网上流传的命令指令) 的键值对,内容为一个 SSH 公钥。
    在 /root/.ssh 文件夹下新建或者修改了 authorized_keys 文件,内容为 Redis 生成的 db 文件,包含上述公钥
    修复建议:
    1.当然就是改密码了,之前的密码太弱了,可能是不是被人家给碰上了,直接就破解了,赶紧改一个复杂一点的密码才是王道,如果你的Redis连密码都没有,那更不行了,看看我的遭遇,赶紧加上密码吧!
    在Redis服务的配置中加入密码项目,默认是注释的,你要手动加入密码,打开配置文件找到:

    #requirepass foobared 
    

    去掉之前的注释,并修改为所需要的密码,保存文件并重启Redis服务。
    启动命令是:

    redis-server --service-install redis.windows-service.conf --loglevel debug
    

    注意 --loglevel debug ,这里是加入了日志的启动方式,也是要在配置文件中进行修改的,打开配置文件找到:

    loglevel verbose
    

    去掉注释并改为:

    loglevel debug
    

    并且由于要存放日志的话则要给存放的Redis日志启用存储路径,在配置文件中搜索”logfile“就可以了,然后给其指定路径:

    logfile "Logs/redis_log.txt"
    

    注意:这里改完后,要启动服务前,在你的Redis的安装目录的根目录下手动创建一个名为"Logs"的文件夹,如果缺少这个文件夹,启动服务的时候会报错。
    2.改端口号,默认的6379端口肯定是不能用了,赶紧在redis的配置文件中修改端口号吧!
    在Redis的配置文件中找到:

    port 6379
    

    改为别的未被占用的端口即可。
    3.禁止一些高危的命令:
    修改Redis的配置文件,加入以下几行命令:

    rename-command FLUSHALL ""
    rename-command FLUSHDB ""
    rename-command KEYS ""
    rename-command CONFIG ""
    rename-command EVAL ""
    

    ﹡以下的一些设置是网上的,我没用用,因为不适合我的情况,仅供参考:
    4.以权限运行Redis服务
    为 Redis 服务创建单独的用户和家目录,并且配置禁止登陆
    5.禁止外网访问 Redis
    修改Redis配置文件,添加或修改:

    bind 127.0.0.1
    

    使得 Redis 服务只在当前主机可用

    总结

    浅薄啊!粗心啊!一切的一切都是因为自己在服务器和Redis安全性这里不够重视而导致的问题,其实只要设置一个复杂一点的密码,估计也不会这么轻易的被入侵了,希望我的遭遇能给你带来一些启发,少走或者不走弯路,赶紧加固自己的Redis安全防御吧!

    相关文章

      网友评论

        本文标题:Redis被入侵之后

        本文链接:https://www.haomeiwen.com/subject/egasnttx.html