網絡游戲的網絡協議設計之防外掛
我們不能期望提供完全安全的通訊,但我們可以讓攻擊者的麻煩大于其獲得
- 任何對發送者的協議body(通訊的實際數據)序列進行的修改都應該被檢測到
- 我們只處理body的發送
- 對于報文的順序和可靠性問題交給底層協議棧去解決.
- 對于篡改報文
- 針對此類攻擊的第一線防御是一個簡單的checksum
- 校驗和的計算范圍需要包括包頭在內的整個報文
- 發送者計算報文的checksum并與報文一起發送給接收者
- 一個完美的校驗和算法能對任意修改過的報文計算出不同的值
- MD5算法是一個經過廣泛測試,可以公開使用的單向hash函數
- 缺點:
- 客戶端程序包含校驗和計算代碼 -> 攻擊者可通過逆向工程獲取校驗和算法 -> 然后對任何消息計算有效校驗和
- 攻擊者可以捕獲有效包并在稍后重發,即packet replay
- packet replay
- 惡意用戶從客戶端捕獲報文,通常通過報文監聽 -> 多次發送 -> 然后以超過游戲允許的速度來執行命令
- 服務器端可設置一個類似每秒一次的計時器來阻止這種攻擊 -> 但是因為可變的網絡延遲 ->導致合服的命令序列被拒絕
- 不希望我們的安全機制將合法玩家當成欺騙者
- 預防報文重放
- 每個報文需要包含一些狀態信息->即使相同的有效body也要有不同的bit pattern
- 一個隨著每個報文發送而累加的計數器之類的辦法就可以做到 -> 但是這種策略使攻擊者能夠很容易預期
- 一個較好的方法是使用一個狀態機為連續的報文生成連續的序列號->算法快速并且足夠復雜
- State = (State + a ) * b
- a和b是仔細挑選出來的整數
- 發送一個報文時-> 發送者生產一個隨機數并將之添加到報文中 -> 同時步進隨機數生成器
- 接收者使用自己的生成器檢查收到報文中的隨機數->如果數字不匹配則表示報文已經被篡改->如果數字匹配,則接收者也步進隨機數生成器以準備接收下一個報文
- 復雜之處
- 發送者和接收者如何初始化并同步他們的狀態機
- 可以使用固定種子啟動各自的狀態機 -> 但是初始報文流的位模式總是一樣 ->因為會成為可被分析的漏洞
- 替代辦法:由服務器使用隨機生產的種子值初始化其狀態機
- 如何在通信中保持狀態機的同步
- 可信連接中 -> 包永遠不會丟失 -> 同步是有保障-
- 當報文丟失或重新排序 -> 情況變的更加復雜
- 如果消息被丟失 -> 發送者的狀態機比接收者的狀態機多步進一次 -> 后來的報文即使合法也都被拒絕
- 簡單解決方案:使用一個在每個報文中發送的真實序列號 -> 通過這個序列號 -> 接收者可以決定需要步進它的狀態機多少次以適合當前報文
- 如果應用程序允許無序發送(即可能會出現先發的數據包會后收到)->較老的狀態機狀態必須被保存以便在被打亂次序的報文抵達后使用
- 如發送順序為A,B,C -> 但是收到的順序是B,A,C
- 則對于如A包的校驗需要將將B的state保存 -> 等到A包到達后用該state校驗
- 發送者和接收者如何初始化并同步他們的狀態機
- 其他技術
- 理想情況下 -> 為了阻擾對有效負載的分析->兩個具有同樣有效負載的報文在其模式上應該有盡可能少的相關性
- 一個簡單的消除兩個集合之間相關性的方法是將其數據與一系列的隨機位進行異或操作(XOR)
- A ^ randomSeed ^ randomSeed = A,如100 ^ 101 = 001 ^ 101 = 100
- 上面描述的報文重發預防中 -> 發送者和接收者已經同步了隨機數生成器 -> 發送者可以為每個報文生成一個隨機數序列 -> 并將之與報文有效負載進行異或操作 -> 接收者生成相同的數字序列并以相同的方式獲取原始數據
- 兩個具有相同長度的報文可能給攻擊者一個報文編碼相似數據的線索 -> 進一步干擾攻擊者 -> 每個報文可以包含一些可變長度的隨機垃圾數據 -> 其僅用來改變報文長度
- 發送者檢測其狀態機決定需要生成并插入多少字節的隨機數作為垃圾數據到發送的報文中
- 接收者只需要忽略垃圾數據
- 增加垃圾數據總量可以進一步隱藏有效負荷 -> 但需要消耗額外的帶寬
- 逆向工程
- 客戶端包括完整的加密算法 -> 總是可以進行逆向工程 -> 這是最難解決的問題 -> 也是任何阻止協議篡改機制的根本弱點 -> 可以采用一些步驟增加逆向工程的難度
牢記你的目標是讓作弊的成本最大化 -> 而非完全禁止作弊
References
- 《Game Programming Gems 1》1.11 # "A Network Protocol for Online games"