防止重放機制

一、API重放攻擊

我們在設(shè)計接口的時候,最怕一個接口被用戶截取用于重放攻擊。重放攻擊是什么呢?就是把你的請求原封不動地再發(fā)送一次,兩次...n次,重放攻擊是二次請求,黑客通過抓包獲取到了請求的HTTP報文,然后黑客自己編寫了一個類似的HTTP請求,發(fā)送給服務(wù)器。也就是說服務(wù)器處理了兩個請求,先處理了正常的HTTP請求,然后又處理了黑客發(fā)送的篡改過的HTTP請求。

如果這個正常邏輯是插入數(shù)據(jù)庫操作,那么一旦插入數(shù)據(jù)庫的語句寫的不好,就有可能出現(xiàn)多條重復(fù)的數(shù)據(jù)。一旦是比較慢的查詢操作,就可能導(dǎo)致數(shù)據(jù)庫堵住等情況。

1.1 重放攻擊的概念:

重放攻擊是計算機世界黑客常用的攻擊方式之一,所謂重放攻擊就是攻擊者發(fā)送一個目的主機已接收過的包,來達(dá)到欺騙系統(tǒng)的目的,主要用于身份認(rèn)證過程。

二、重放攻擊的防御方案

2.1 基于timestamp方案

每次HTTP請求,都需要加上timestamp參數(shù),然后把timestamp和其他參數(shù)一起進(jìn)行數(shù)字簽名。因為一次正常的HTTP請求,從發(fā)出到達(dá)服務(wù)器一般都不會超過60s,所以服務(wù)器收到HTTP請求之后,首先判斷時間戳參數(shù)與當(dāng)前時間相比較,是否超過了60s,如果超過了則認(rèn)為是非法的請求。

假如黑客通過抓包得到了我們的請求url:
http://www.itdecent.cn?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];
    }
}

一般情況下,黑客從抓包重放請求耗時遠(yuǎn)遠(yuǎn)超過了60s,所以此時請求中的time參數(shù)已經(jīng)失效了。

如果黑客修改time參數(shù)為當(dāng)前的時間戳,則sign參數(shù)對應(yīng)的數(shù)字簽名就會失效,因為黑客不知道token值,沒有辦法生成新的數(shù)字簽名。

但這種方式的漏洞也是顯而易見的,如果在60s之內(nèi)進(jìn)行重放攻擊,那就沒辦法了,所以這種方式不能保證請求僅一次有效。

2.2 基于nonce方案

nonce是僅一次有效的隨機字符串,要求每次請求時,該參數(shù)要保證不同,所以該參數(shù)一般與時間戳有關(guān),我們這里為了方便起見,直接使用時間戳作為種子,隨機生成16位的字符串,作為nonce參數(shù)。

我們將每次請求的nonce參數(shù)存儲到一個redis中。 每次處理HTTP請求時,首先判斷該請求的nonce參數(shù)是否在redis中,如果存在則認(rèn)為是非法請求。

假如黑客通過抓包得到了我們的請求url:
http://www.itdecent.cn?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參數(shù)在首次請求時,已經(jīng)被存儲到了服務(wù)器上的redis中,再次發(fā)送請求會被識別并拒絕。
nonce參數(shù)作為數(shù)字簽名的一部分,是無法篡改的,因為黑客不清楚token,所以不能生成新的sign。

這種方式也有很大的問題,那就是存儲nonce的redis會越來越大,驗證nonce是否存在redis中的耗時會越來越長。我們不能讓nonce集合無限大,所以需要定期清理該“集合”,但是一旦該集合被清理,我們就無法驗證被清理了的nonce參數(shù)了。也就是說,假設(shè)該集合平均1天清理一次的話,我們抓取到的該url,雖然當(dāng)時無法進(jìn)行重放攻擊,但是我們還是可以每隔一天進(jìn)行一次重放攻擊的。而且存儲24小時內(nèi),所有請求的“nonce”參數(shù),也是一筆不小的開銷。

2.2 基于timestamp+nonce方案

我們常用的防止重放的機制是使用timestamp和nonce來做的重放機制。

每個請求帶的時間戳不能和當(dāng)前時間超過一定規(guī)定的時間(60s)。這樣請求即使被截取了,你也只能在60s內(nèi)進(jìn)行重放攻擊,過期失效。
但是攻擊者還有60s的時間攻擊。所以我們就需要加上一個nonce隨機數(shù),防止60s內(nèi)出現(xiàn)重復(fù)請求。

timstamp參數(shù)對于超過60s的請求,都認(rèn)為非法請求;
redis存儲60s內(nèi)的nonce參數(shù)的集合,60s內(nèi)重復(fù)則認(rèn)為是非法請求。

http://www.itdecent.cn?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");

三、服務(wù)端實現(xiàn)流程

服務(wù)端第一次在接收到這個nonce的時候做下面行為:
1 去redis中查找是否有key為nonce:{nonce}的string
2 如果沒有,則創(chuàng)建這個key,把這個key失效的時間和驗證time失效的時間一致,比如是60s。
3 如果有,說明這個key在60s內(nèi)已經(jīng)被使用了,那么這個請求就可以判斷為重放請求。

3.1 示例

那么比如,下面這個請求:

http://www.itdecent.cn?uid=3535353535353535&time=1543993979284&nonce=VUmVZgKxkpk_rabQ&sign=da5dba49e4211df48bb5b619358c0db0

time,nonce,sign都是為了簽名和防重放使用。

time是發(fā)送接口的時間,nonce是隨機串,sign是對uid,time,nonce。簽名的方法可以是md5({秘要}key1=val1&key2=val2&key3=val3...)

服務(wù)端接到這個請求:
1 先驗證sign簽名是否合理,證明請求參數(shù)沒有被中途篡改
2 再驗證time是否過期,證明請求是在最近60s被發(fā)出的
3 最后驗證nonce是否已經(jīng)有了,證明這個請求不是60s內(nèi)的重放請求

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 我們在設(shè)計接口的時候,最怕一個接口被用戶截取用于重放攻擊。重放攻擊是什么呢?就是把你的請求原封不動地再發(fā)送一次,兩...
    devLionel閱讀 1,482評論 0 2
  • 說說API的防重放機制 我們在設(shè)計接口的時候,最怕一個接口被用戶截取用于重放攻擊。重放攻擊是什么呢?就是把你的請求...
    MxlZlh閱讀 2,354評論 2 18
  • 關(guān)于Mongodb的全面總結(jié) MongoDB的內(nèi)部構(gòu)造《MongoDB The Definitive Guide》...
    中v中閱讀 32,284評論 2 89
  • 1.spring對mybatis的事務(wù)管理是怎么支持的 一、事務(wù)的基本原理 Spring事務(wù)的本質(zhì)其實就是數(shù)據(jù)庫對...
    __游離__閱讀 1,202評論 6 7
  • 作者:奧黛莉.潘恩 譯者:劉清彥 繪者:茹絲.哈波 出版社:上誼 推薦人:猴子老師 猴子老師導(dǎo)讀: 「面對改變的不...

友情鏈接更多精彩內(nèi)容