防止重放机制

一、API重放攻击

我们在设计接口的时候,最怕一个接口被用户截取用于重放攻击。重放攻击是什么呢?就是把你的请求原封不动地再发送一次,两次...n次,重放攻击是二次请求,黑客通过抓包获取到了请求的HTTP报文,然后黑客自己编写了一个类似的HTTP请求,发送给服务器。也就是说服务器处理了两个请求,先处理了正常的HTTP请求,然后又处理了黑客发送的篡改过的HTTP请求。

如果这个正常逻辑是插入数据库操作,那么一旦插入数据库的语句写的不好,就有可能出现多条重复的数据。一旦是比较慢的查询操作,就可能导致数据库堵住等情况。

1.1 重放攻击的概念:

重放攻击是计算机世界黑客常用的攻击方式之一,所谓重放攻击就是攻击者发送一个目的主机已接收过的包,来达到欺骗系统的目的,主要用于身份认证过程。

二、重放攻击的防御方案

2.1 基于timestamp方案

每次HTTP请求,都需要加上timestamp参数,然后把timestamp和其他参数一起进行数字签名。因为一次正常的HTTP请求,从发出到达服务器一般都不会超过60s,所以服务器收到HTTP请求之后,首先判断时间戳参数与当前时间相比较,是否超过了60s,如果超过了则认为是非法的请求。

假如黑客通过抓包得到了我们的请求url:
//www.greatytc.com/?uid=3535353535353535&time=1543991604448&sign=eaba21f90e635c22d2d775731ec03a92
其中

long uid = 3535353535353535L;
String token = "fewgjiwghwoi3ji4oiwjo34ir4erojwk";
long time = new Date().getTime();//1543991604448
String sign = MD5Utils.MD5Encode("uid=" + uid + "&time=" + time + token,"utf8");
public class MD5Utils {
    private static final String hexDigIts[] = {"0","1","2","3","4","5","6","7","8","9","a","b","c","d","e","f"};

    /**
     * MD5加密
     * @param origin 字符
     * @param charsetname 编码
     * @return
     */
    public static String MD5Encode(String origin, String charsetname){
        String resultString = null;
        try{
            resultString = new String(origin);
            MessageDigest md = MessageDigest.getInstance("MD5");
            if(null == charsetname || "".equals(charsetname)){
                resultString = byteArrayToHexString(md.digest(resultString.getBytes()));
            }else{
                resultString = byteArrayToHexString(md.digest(resultString.getBytes(charsetname)));
            }
        }catch (Exception e){
        }
        return resultString;
    }


    public static String byteArrayToHexString(byte b[]){
        StringBuffer resultSb = new StringBuffer();
        for(int i = 0; i < b.length; i++){
            resultSb.append(byteToHexString(b[i]));
        }
        return resultSb.toString();
    }

    public static String byteToHexString(byte b){
        int n = b;
        if(n < 0){
            n += 256;
        }
        int d1 = n / 16;
        int d2 = n % 16;
        return hexDigIts[d1] + hexDigIts[d2];
    }
}

一般情况下,黑客从抓包重放请求耗时远远超过了60s,所以此时请求中的time参数已经失效了。

如果黑客修改time参数为当前的时间戳,则sign参数对应的数字签名就会失效,因为黑客不知道token值,没有办法生成新的数字签名。

但这种方式的漏洞也是显而易见的,如果在60s之内进行重放攻击,那就没办法了,所以这种方式不能保证请求仅一次有效。

2.2 基于nonce方案

nonce是仅一次有效的随机字符串,要求每次请求时,该参数要保证不同,所以该参数一般与时间戳有关,我们这里为了方便起见,直接使用时间戳作为种子,随机生成16位的字符串,作为nonce参数。

我们将每次请求的nonce参数存储到一个redis中。 每次处理HTTP请求时,首先判断该请求的nonce参数是否在redis中,如果存在则认为是非法请求。

假如黑客通过抓包得到了我们的请求url:
//www.greatytc.com/?uid=3535353535353535&nonce=RLLUammMSInlrNWb&sign=d2f7406dfdeea3561f753d9e0d1dc320

long uid = 3535353535353535L;
String token = "fewgjiwghwoi3ji4oiwjo34ir4erojwk";
long time = new Date().getTime();//1543993280840
String nonce = RandomUtils.getRandomChar(time);
String sign = MD5Utils.MD5Encode("uid=" + uid + "&nonce=" + nonce + token,"utf8");
public class RandomUtils {

    public static String getRandomChar(long time){
        Random random = new Random(time);
        StringBuffer sb = new StringBuffer();
        for(int i = 0; i < 16; i++){
            char c = (char)(random.nextLong() % 26 + 97);
            sb.append(c);
        }
        return sb.toString();
    }
}

nonce参数在首次请求时,已经被存储到了服务器上的redis中,再次发送请求会被识别并拒绝。
nonce参数作为数字签名的一部分,是无法篡改的,因为黑客不清楚token,所以不能生成新的sign。

这种方式也有很大的问题,那就是存储nonce的redis会越来越大,验证nonce是否存在redis中的耗时会越来越长。我们不能让nonce集合无限大,所以需要定期清理该“集合”,但是一旦该集合被清理,我们就无法验证被清理了的nonce参数了。也就是说,假设该集合平均1天清理一次的话,我们抓取到的该url,虽然当时无法进行重放攻击,但是我们还是可以每隔一天进行一次重放攻击的。而且存储24小时内,所有请求的“nonce”参数,也是一笔不小的开销。

2.2 基于timestamp+nonce方案

我们常用的防止重放的机制是使用timestamp和nonce来做的重放机制。

每个请求带的时间戳不能和当前时间超过一定规定的时间(60s)。这样请求即使被截取了,你也只能在60s内进行重放攻击,过期失效。
但是攻击者还有60s的时间攻击。所以我们就需要加上一个nonce随机数,防止60s内出现重复请求。

timstamp参数对于超过60s的请求,都认为非法请求;
redis存储60s内的nonce参数的集合,60s内重复则认为是非法请求。

//www.greatytc.com/?uid=3535353535353535&time=1543993979284&nonce=VUmVZgKxkpk_rabQ&sign=da5dba49e4211df48bb5b619358c0db0

long uid = 3535353535353535L;
String token = "fewgjiwghwoi3ji4oiwjo34ir4erojwk";
long time = new Date().getTime();//1543993979284
String nonce = RandomUtils.getRandomChar(time);
String sign = MD5Utils.MD5Encode("uid=" + uid + "&time" + time +"&nonce=" + nonce + token,"utf8");

三、服务端实现流程

服务端第一次在接收到这个nonce的时候做下面行为:
1 去redis中查找是否有key为nonce:{nonce}的string
2 如果没有,则创建这个key,把这个key失效的时间和验证time失效的时间一致,比如是60s。
3 如果有,说明这个key在60s内已经被使用了,那么这个请求就可以判断为重放请求。

3.1 示例

那么比如,下面这个请求:

//www.greatytc.com/?uid=3535353535353535&time=1543993979284&nonce=VUmVZgKxkpk_rabQ&sign=da5dba49e4211df48bb5b619358c0db0

time,nonce,sign都是为了签名和防重放使用。

time是发送接口的时间,nonce是随机串,sign是对uid,time,nonce。签名的方法可以是md5({秘要}key1=val1&key2=val2&key3=val3...)

服务端接到这个请求:
1 先验证sign签名是否合理,证明请求参数没有被中途篡改
2 再验证time是否过期,证明请求是在最近60s被发出的
3 最后验证nonce是否已经有了,证明这个请求不是60s内的重放请求

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 196,397评论 5 462
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 82,585评论 2 373
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 143,521评论 0 325
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,645评论 1 267
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,489评论 5 358
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,366评论 1 274
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,787评论 3 387
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,432评论 0 255
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,732评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,775评论 2 314
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,550评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,393评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,802评论 3 300
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,036评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,324评论 1 253
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,757评论 2 342
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,958评论 2 336

推荐阅读更多精彩内容

  • 我们在设计接口的时候,最怕一个接口被用户截取用于重放攻击。重放攻击是什么呢?就是把你的请求原封不动地再发送一次,两...
    devLionel阅读 1,422评论 0 2
  • 说说API的防重放机制 我们在设计接口的时候,最怕一个接口被用户截取用于重放攻击。重放攻击是什么呢?就是把你的请求...
    MxlZlh阅读 2,284评论 2 18
  • 关于Mongodb的全面总结 MongoDB的内部构造《MongoDB The Definitive Guide》...
    中v中阅读 31,864评论 2 89
  • 1.spring对mybatis的事务管理是怎么支持的 一、事务的基本原理 Spring事务的本质其实就是数据库对...
    __游离__阅读 1,142评论 6 7
  • 作者:奥黛莉.潘恩 译者:刘清彦 绘者:茹丝.哈波 出版社:上谊 推荐人:猴子老师 猴子老师导读: 「面对改变的不...