一、概要
在安全機制的設計過程中进宝,通常考慮多種安全機制枷恕,其中防重放機制是眾多安全機制中較為重要的一個党晋。在尤其在控制信令傳輸環(huán)節(jié),防重放攻擊尤為重要徐块。目前項目中使用到的防重放機制多為流水編號機制未玻,該機制實現(xiàn)簡單,但無法滿足復雜網(wǎng)絡環(huán)境下存在丟包的場景胡控。該案例介紹了兩種滑動窗口的設計思路和實現(xiàn)扳剿,以滿足復雜網(wǎng)絡環(huán)境下丟包和包不連續(xù)的場景。
二铜犬、滑動窗口算法描述
(一)標準滑動窗口算法
窗口右邊界為已接收的舞终,且最大的數(shù)據(jù)包序列號(Max Sequence Number),窗口左邊界為最大數(shù)據(jù)包序列號減去窗口大小得到的數(shù)值(Max Sequence Number - Windows Size)癣猾。
假設:窗口大小為32敛劝,Max Sequence Number為100,窗口如下:
|_____________32_____________|
68 ?????????100當新接收的數(shù)據(jù)包的Sequence Number 大于68且小于100時纷宇,數(shù)據(jù)包合法夸盟,且窗口中對應的位置設置為1,說明該序號已接收像捶。當該序號再次出現(xiàn)時上陕,認為非法桩砰。如當前Sequence Number為78:
|________1_____32_____________|
68 ? ??????? 100當新接收的數(shù)據(jù)包的Sequence Number 小于68時,認為該數(shù)據(jù)包非法释簿。
當新接收的數(shù)據(jù)包的Sequence Number 大于100亚隅,小于100+32時,數(shù)據(jù)包合法庶溶,并將窗口右邊界設置為該數(shù)據(jù)包Sequence Number 煮纵, 如當前Sequence Number 為120:
|_____________32_____________|
88 ?? ????????120當新接收的數(shù)據(jù)包的Sequence Number 大于100+32時,數(shù)據(jù)包不合法偏螺。
(二)無右邊界滑動窗口算法
該算法對標準算法第五步進行調(diào)整: 當新接收的數(shù)據(jù)包Sequence Number大于窗口大小行疏,右邊界設置為新接收的Sequence Number,窗口最右端bit設置為1套像,其余設置為0酿联。
該算法設計背景: 在部分UDP連接情況下,數(shù)據(jù)包會有連續(xù)發(fā)送失敗的場景夺巩,導致接收端接收的數(shù)據(jù)包Sequence Number驟然增大贞让,防重放機制會阻擋正常數(shù)據(jù)包的接收,因此需關閉滑動窗口右邊界限制劲够。
該算法引入的風險:新方案關閉滑動窗口右邊界限制震桶,當同一Session下有大于當前Sequence Number的數(shù)據(jù)包混入休傍,會將滑動窗口向右推移征绎,導致正常數(shù)據(jù)無法接收。
風險解決方案:Sequence Number在Session會話有效期內(nèi)不會重新計數(shù)即可磨取。
三人柿、滑動窗口算法運行要求
防重放程序必須在簽名校驗完成且通過后才能開始運行,防止窗口被惡意推動忙厌,導致業(yè)務處理中斷凫岖。
四、算法實現(xiàn)
考慮到滑動窗口計算需要較短的處理時間逢净,因此采用bitmap算法設計滑動窗口實現(xiàn)哥放。并提供固定窗口大小算法和動態(tài)設定窗口大小算法,來滿足復雜環(huán)境下業(yè)務對窗口動態(tài)調(diào)整的需求爹土。
固定窗口大小
// 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;
}
動態(tài)窗口大小
// 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;
}