加密,防刷,惩罚处理方案(初搞)

                   这个方案涉及到客户端加密,服务器解密,防外挂,减少一定量的刷机行为

分析:

以泰国玩家为案例,之前文丰做了比较详细的分析
http://192.168.16.108:3000/boards/4/topics/707
总结为:1,利用工具修改客户端数据;
        2,找到某些漏洞进行刷服务器;
        3, 不排除有玩家进行ddos攻击;
        4,外挂出现。
目前DAU是75W水平,1 nginx,25 resin,10 memcached,12 mysqldb.

设想:

首先大家想到的是加密,客户端数据加密,即对客户端传送给服务器的数据加密;
数据加密了,之前在数组里的token的就直接可以用玩家uid,做会话功能。
取客户端时间戳作为刷机校验码:把时间戳携带在每个返回的数组里,服务器端的MC
里对应其上次带回的时间戳,以<key=uid,value=client_time>,时间精确到毫秒级别,
服务器拿着此次返回的时间戳与上次的时间戳进行验证,如果发现两个值的差值是0
到某个毫秒级别,则判这次请求非法,则返回,同时记录日志,如果同一个用户发生
这种操作达到某个量级,则触发惩罚机制。

考虑到性能和服务器压力,我们可以对不同的命令做不同的处理:

命令分类:  1,初始化命令init_cmd,如玩家初始化游戏时,会时间或极短时间内发送不同的命令;
             2,普通命令common_cmd:相对一些简单操作,如读缓存等,不涉及到事务或操作重要数据的命令;
             3,重量级命令weight_cmd:相对一些复杂操作,如耗CPU,内存,像决对性的对DB的查询,或大量对像的修改命令;
             4,并发命令concurrent_cmd:涉及到事务的,数据重要的操作,如买卖道器物品,兑换,升级等。
 
  命令分类是分散来自不同的命令我们给出不同机制的处理,相对减轻服务器压力,通过分析日志,记录玩家非常规操作
行为,按命令级别做出不同级别的处理,同时依据不同的命令级别我们选用不同的控制频率 Ft。


设计:

flash client:
      //客户端数组
      String[] params = [cmd,param1,param2....,client_time,uid];
      cmd 命令,param* 参数,client_time 客户端时间戳(精确到毫秒),uid 用户ID
      客户端对数组DES加密成一个字符串参数 传给服务器端;(开发中,分出debug模式与正式版模式,利于开发人员的调试)
       params-------DES加密------>"xxxxxxxxxxxxxxxxxxxxx";
      加密KEY,是固定不变,还是按时间如周,或月的频率改变,大家可讨论,如果是变化的注意时间边界值的处理.
     
java server:

      解密客户端参数:"xxxxxxxxxxxxxxxxxxxxx"-----解密---->params = [cmd,param1,param2....,client_time,uid];
     
      用户ID:                     uid
      当前用户请求业务命令:       cmd
      此次请求客户端时间戳:client_tim
      上次请求时间戳:              tc
      命令集:               xxxxx_cmd
      可刷的时间间隔:              Ft
      惩罚度:                      Pn
      Memcached:                   mc

      switch(cmd)

            init_cmd:
                     //按DAU为150W分析,玩家每天登录游戏必须的操作的命令;
                     //按每位玩家平均每天20次操作,数据级很大,暂不作监控处理;  
          common_cmd:
                    //因为是只做缓存查询操作,只要检验出在极暂时间内发请求,则判它为刷机行为,并返回,不提供服务;
                    Ft=f1;
          weight_cmd:
                   //同common_cmd处理,并进入惩罚机制,惩罚别级:轻;Pn=p1;
                   Ft=f2;
      concurrent_cmd:
                  //同common_cmd处理,并进入惩罚机制,惩罚别级:重;Pn=p2;
                  Ft=f3;
 
校验机制:

    long tc=mc.get(uid);//取出该用户的上一次请求的客户端时间戳;
    if(tc == null){//如果因为第一次访问或过期失效为null;
       mc.set(uid,client_time);//把此次从客户端返回的时间戳存入mc;
       return 0;
    }else if(client_time-tc==0){//如果上次存入mc的时间戳和此次返回的时间戳相等,则表示为非法请求,并返回;
       return 2;
    }else if(client_time-tc < Ft){
       mc.replace(uid,client_time);
       return 1;
    }else{
      mc.replace(uid,client_time);//把此次从客户端返回的时间戳存入mc;
      return 0;
    }         
   依次返回结果值为:0,1,2;

惩罚机制:

    设校验机制返回的结果值为 f  (0<=f<=2);
    惩罚度:                 pn (p1>p2>2)

    if(f>0){
      long pn= mc.addOrIncr("PUNISH_"+uid,f);
      if(cmd is weight_cmd && pn>=p1){
        //实现具体的惩罚规则;
        //限时登录
        //归零
        mc.addOrIncr("PUNISH_"+uid,0);
       }else if(cmd is concurrent_cmd && pn>=p2){
       //实现具体的惩罚规则;
       //限时时间长或罚金币 E币 或封号处理
       ////归零
       mc.addOrIncr("PUNISH_"+uid,0);
      }

    }


问题:

    因为每个玩家的校验码存放在mc里,又基于我们的系统是分布式架构,所以去mc里取 tc(cliemt_time) 时会存在脏读情况,
是否考虑每个resin里存放一个当天有效的ConcurrentHashMap<uid,client_time> resin_map.
校验机制如下:

    long tc=resin_map.get(uid);//取出该用户的上一次请求的客户端时间戳;(从本地resin内存里取)
    if(tc == null){//如果本地取出的值为null;
       tc=mc.get(uid);//则再去mc里取;
       if(tc == null){//如果mc里也为null;
        mc.set(uid,client_time);//把此次从客户端返回的时间戳存入mc;
        resin_map.set(uid,cliemt_time);//放入resin map;
        return 0;
       }else if(client_time-tc==0){//如果上次存入mc的时间戳和此次返回的时间戳相等,则表示为非法请求,并返回;
         return 2;
       }else if(client_time - tc < Ft){
        mc.set(uid,client_time);
        return 1;
       }
    }else if(tc-client_time==0){//如果上次存入mc的时间戳和此次返回的时间戳相等,则表示为非法请求,并返回;
       return 2;
    }else if(client_time-tc < Ft){
      mc.set(uid,client_time);
      resin_map.set(uid,cliemt_time);
      return 1;
    }else{
       tc=mc.get(uid);//则再去mc里取;
       if(tc == null){//如果mc里为null;
         mc.set(uid,client_time);//把此次从客户端返回的时间戳存入mc;
         resin_map.set(uid,cliemt_time);//再存主本地resin map;
         return 0;
       }else if(client_time-tc==0){//如果上次存入mc的时间戳和此次返回的时间戳相等,则表示为非法请求,并返回;
         return 2;
       }else if(client_time-tc < Ft){
        mc.set(uid,client_time);
        resin_map.set(uid,cliemt_time);
        return 1;
       }
    }

以上想法是牺牲性能的情况下换取了进一步控制并发,请求在短时间内访问同一台服务器的情况下发挥的作用很大。
结尾:以上主要是对防刷问题提出的方案,由于客户端加密的引入,对以下问题都能有些防范,

  1.玩家修改客户端cookie值;
  2.玩家找到平台调用游戏的url,修改其中的sessionkey和uid来冒充其它玩家;
  3.利用charles修改传递的参数;
  4.外挂。

对于ddos的攻击,可在nginx对外来IP做处理,
下面是对人人网上的阳光牧场 卖蔬菜做的10000次并发 返回的一些结果中有部分nginx做了处理

猜你喜欢

转载自wangmingming2008.iteye.com/blog/1025084