美文网首页
滑动窗口防重放算法使用经验分享

滑动窗口防重放算法使用经验分享

作者: 王小宇gg | 来源:发表于2020-03-06 15:23 被阅读0次

    一、概要

    在安全机制的设计过程中,通常考虑多种安全机制,其中防重放机制是众多安全机制中较为重要的一个。在尤其在控制信令传输环节,防重放攻击尤为重要。目前项目中使用到的防重放机制多为流水编号机制,该机制实现简单,但无法满足复杂网络环境下存在丢包的场景。该案例介绍了两种滑动窗口的设计思路和实现,以满足复杂网络环境下丢包和包不连续的场景。

    二、滑动窗口算法描述

    (一)标准滑动窗口算法

    1. 窗口右边界为已接收的,且最大的数据包序列号(Max Sequence Number),窗口左边界为最大数据包序列号减去窗口大小得到的数值(Max Sequence Number - Windows Size)。
      假设:窗口大小为32,Max Sequence Number为100,窗口如下:
      |_____________32_____________|
      68          100

    2. 当新接收的数据包的Sequence Number 大于68且小于100时,数据包合法,且窗口中对应的位置设置为1,说明该序号已接收。当该序号再次出现时,认为非法。如当前Sequence Number为78:
      |________1_____32_____________|
      68   \color{red}{78}         100

    3. 当新接收的数据包的Sequence Number 小于68时,认为该数据包非法。

    4. 当新接收的数据包的Sequence Number 大于100,小于100+32时,数据包合法,并将窗口右边界设置为该数据包Sequence Number , 如当前Sequence Number 为120:
      |_____________32_____________|
      88            120

    5. 当新接收的数据包的Sequence Number 大于100+32时,数据包不合法。

    (二)无右边界滑动窗口算法

    该算法对标准算法第五步进行调整: 当新接收的数据包Sequence Number大于窗口大小,右边界设置为新接收的Sequence Number,窗口最右端bit设置为1,其余设置为0。
    该算法设计背景: 在部分UDP连接情况下,数据包会有连续发送失败的场景,导致接收端接收的数据包Sequence Number骤然增大,防重放机制会阻挡正常数据包的接收,因此需关闭滑动窗口右边界限制。
    该算法引入的风险:新方案关闭滑动窗口右边界限制,当同一Session下有大于当前Sequence Number的数据包混入,会将滑动窗口向右推移,导致正常数据无法接收。
    风险解决方案:Sequence Number在Session会话有效期内不会重新计数即可。

    三、滑动窗口算法运行要求

    防重放程序必须在签名校验完成且通过后才能开始运行,防止窗口被恶意推动,导致业务处理中断。

    四、算法实现

    考虑到滑动窗口计算需要较短的处理时间,因此采用bitmap算法设计滑动窗口实现。并提供固定窗口大小算法和动态设定窗口大小算法,来满足复杂环境下业务对窗口动态调整的需求。

    固定窗口大小
    // C++ program to demonstrate various functionality of bitset
    #include <bits/stdc++.h>
    using namespace std;
     
    enum {
        ReplayWindowSize = 128  //如果需要将窗口大小设置为1,此处设置ReplayWindowSize = 2
    };
    typedef unsigned long u_long;
    u_long lastSeq = 0;
    bitset<ReplayWindowSize> bitmap;
     
    int ChkReplayWindow(u_long seq) {
        u_long diff;
     
        if (seq == 0) return 0;             /* first == 0 or wrapped */
        if (seq > lastSeq) {                /* new larger sequence number */
            diff = seq - lastSeq;
            if (diff < ReplayWindowSize) {  /* In window */
                bitmap <<= diff;
                bitmap |= 1;                /* set bit for this packet */
            //} else return 0;          /* 启用窗口右边界限制 */
            } else bitmap = 1;        /*关闭窗口右边界限制 */
            lastSeq = seq;
            return 1;                       /* larger is good */
        }
        diff = lastSeq - seq;
        if (diff >= ReplayWindowSize) return 0; /* too old or wrapped */
        if (bitmap[diff] == 1) return 0; /* already seen */
        bitmap |= ((u_long)1 << diff);              /* mark as seen */
        return 1;                           /* out of order but good */
    }
     
    char string_buffer[512];
    #define STRING_BUFFER_SIZE sizeof(string_buffer)
     
    int main()
    {
        cout << bitmap << endl; // 00000000000000000000000000000000
     
        int result;
        u_long last, current;
        uint64_t bits;
     
        printf("Input initial state (bits in hex, last msgnum):\n");
        if (!fgets(string_buffer, STRING_BUFFER_SIZE, stdin)) exit(0);
        sscanf(string_buffer, "%lx %lu", &bits, &last);
        if (last != 0)
            bits |= 1;
        bitmap = bits;
        lastSeq = last;
        cout << "bits: " << bitmap << "\n";
        printf("last:%lu\n", lastSeq);
        printf("Input value to test (current):\n");
     
        while (1) {
            if (!fgets(string_buffer, STRING_BUFFER_SIZE, stdin)) break;
            sscanf(string_buffer, "%lu", ¤t);
            result = ChkReplayWindow(current);
            printf("%-3s", result ? "OK " : "BAD ");
            cout << "bits: " << bitmap << "\n";
            printf("last:%lu\n",lastSeq);
        }
        return 0;
    } 
    
    动态窗口大小
    // C++ program to demonstrate various functionality of bitset
    #include <bits/stdc++.h>
    using namespace std;
     
    enum {
        ReplayWindowSize = 8    //如果需要将窗口大小设置为1,此处设置ReplayWindowSize = 2
    };
    typedef unsigned long u_long;
    u_long lastSeq = 0;
    std::vector<bool> bitmap;
     
    void LeftShift(int n){
        for (int i = 0;i < n;i++)
        {
            bitmap.insert(bitmap.begin(),0);
            bitmap.erase(bitmap.end());
        }
    }
     
    void PrintBit(){
        for (int i = 0; i<bitmap.size();i++)
        {
            std::cout << bitmap[i] << ",";
        }
        std::cout << std::endl;
    }
     
    int ChkReplayWindow(u_long seq) {
        u_long diff;
     
        if (seq == 0) return 0;             /* first == 0 or wrapped */
        if (seq > lastSeq) {                /* new larger sequence number */
            diff = seq - lastSeq;
            if (diff < ReplayWindowSize) {  /* In window */
                LeftShift(diff);
                bitmap[0] = 1;                /* set bit for this packet */
            //} else return 0;          /*启用窗口右边界限制*/
            } else bitmap.clear();bitmap.resize(ReplayWindowSize);bitmap[0] = 1;  /*关闭窗口右边界限制 */
            lastSeq = seq;
            return 1;                       /* larger is good */
        }
        diff = lastSeq - seq;
        if (diff >= ReplayWindowSize) return 0; /* too old or wrapped */
        if (bitmap[diff] == 1) return 0; /* already seen */
        bitmap[diff] = 1;              /* mark as seen */
        return 1;                           /* out of order but good */
    }
     
    char string_buffer[512];
    #define STRING_BUFFER_SIZE sizeof(string_buffer)
     
    int main()
    {
        bitmap.resize(ReplayWindowSize);
        PrintBit(); // 00000000000000000000000000000000
     
        int result;
        u_long last, current;
        uint64_t bits;
     
        printf("Input initial state (last msgnum):\n");
        if (!fgets(string_buffer, STRING_BUFFER_SIZE, stdin)) exit(0);
        sscanf(string_buffer, "%lu", &last);
        if (last != 0)
            bitmap[0] = 1;
     
        lastSeq = last;
        PrintBit();
        printf("last:%lu\n", lastSeq);
        printf("Input value to test (current):\n");
     
        while (1) {
            if (!fgets(string_buffer, STRING_BUFFER_SIZE, stdin)) break;
            sscanf(string_buffer, "%lu", ¤t);
            result = ChkReplayWindow(current);
            printf("%-3s", result ? "OK " : "BAD ");
            PrintBit();
            //cout << "bits: " << bitmap << "\n";
            printf("last:%lu\n",lastSeq);
        }
        return 0;
    }
    
    

    相关文章

      网友评论

          本文标题:滑动窗口防重放算法使用经验分享

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