iT邦幫忙

2022 iThome 鐵人賽

DAY 16
3
Modern Web

30個遊戲程設的錦囊妙計系列 第 16

Trick 15: 把Hash函數帶進遊戲玩

  • 分享至 

  • xImage
  •  

哈希函數(Hash)對我而言是個熟悉卻又陌生的老朋友,因為在開發各種系統與遊戲的人生中,一直少不了它的陪伴,但始終沒有親手實作這些函數。

Hash Function的中文名字有很多個,音譯是哈希函數,數學稱散列函數,在電腦科學叫雜湊函數。

Hash能幹啥

常見的Hash函數有MD5、SHA-1、SHA-2、SHA-3等。不過不管是哪種演算法,Hash的目標只有一個,就是把一個任意長度的資料壓縮成固定長度的資料,並且保證這個壓縮的結果有下面兩個特性,

  1. 不同資料壓出來的結果會不一樣(抗碰撞)。
  2. 無法由壓出來的資料去反推原本資料的內容(不可逆)。

不過目標歸目標,實際上真的要達到一對一的原資料和壓縮資料是很困難的。MD5雖然經由MD2和MD4發展而來,在一段時間中都很勝任Hash給的任務,但在1996年後被證實可以加以破解,所以後來又發展了SHA系列的Hash函數。

這裏所謂的破解,並不是真的有方法可以在看到MD5壓過的資料就能反推回去原本資料長的樣子。

MD5最後壓縮出來的結果是一個16byte的資料,轉換成16進位後是32個字元,最多就是https://chart.googleapis.com/chart?cht=tx&chl=8%5E%7B16%7D 種可能,所以破解Hash的方法,就是想辦法用各種資料去MD5一下,湊齊MD5所有可能產出的結果,把這個結果儲存成一張表。以後只要看到有個人的密碼被Hash成32位的字串,我只要去查個表,就知道用哪個密碼可以產生出同樣的Hash字串,那不就可以駭入別人的帳號?當然我是絕對不會去這樣做就是了。

不過現在的網站在Hash的時候都會加鹽(salt),也就是在Hash前,先把原密碼加上一些秘密字串再去Hash。如此一來,即使查表法也沒辦法破解密碼了。

雖然MD5存在著一些缺陷,但是因為MD5的效率高,以我們製作遊戲的目的,MD5就能滿足我們絕大部分的需求,所以後面的文章都是以MD5為例來說明。對MD5不放心的同學也可以自己改成SHA2之類的演算法,反正都是套用現成的函式庫,沒什麼差別。

SHA-1也被證實能被破解(google在2018年幹的好事),而SHA-2使用的是和SHA-1同樣的演算法,只是長度較長較難破解。

遊戲可以怎麼玩Hash

今天的文章不會有程式範例,純粹是啟發同學們如何使用Hash的靈感,所以請大家放輕鬆看下去。

明天會延伸Hash這個主題,同學們若想看使用Hash的範例程式,可以等著明天的示範專案。

產生連線玩家的ID

在玩家連線進入遊戲後,伺服器就會給玩家這次登入連線的ID識別代碼。這個識別代碼可以由MD5來幫忙產生。

let playerID = MD5.hash(user.account + Date.now() + Math.random());

CG提供的MD5函式庫的原始碼是來自著名的Joseph Myers's high-performance function

因為我們給MD5.hash()的參數是唯一的(玩家的帳號各自不同,也不太可能同一個玩家在準準同一時刻登入兩次,最後再加一個亂數來掛保證),所以得到的playerID也是唯一的,而且這個ID只會有固定32個字元,是不是很好地應用了MD5的特性呀!

玩家上傳的檔案

我們可以把玩家上傳的圖像、音樂、文件都用MD5.hash()出一個字串,然後把檔案連同這個Hash字串上傳到伺服器。在伺服器上可以用這個Hash字串檢查玩家有沒有盜用別人的圖像,因為同一張圖Hash出來的字串會一樣。

存檔時也可以使用Hash字串作為檔名,避免使用玩家自訂的檔名。因為從不同系統上傳的檔案,有可能包含各種奇奇怪怪編碼的檔名,讓檔案管理變複雜。

驗證同步資料的內容

連線遊戲的時候,在其中一位玩家把訊息發出去之前,遊戲先將整個訊息用MD5.hash()一下,並綁在訊息內容的後面再傳出去。

另外一台電腦上的玩家收到這則訊息後,把這則訊息的內容也用MD5.hash()一下,比對這個hash的結果和訊息夾帶的hash是否相同。如果發現這兩個hash不一樣,那就表示這則訊息在傳遞的過程中被人篡改了。

儲存遊戲進度

同樣的道理,在儲存遊戲進度後,有些玩家會找到儲存進度的檔案(在網頁遊戲中,可能是cookie或localStorage),然後進行數字篡改。這時,如果進度檔裏面有著把進度Hash出來的字串,那這些取巧的玩家就無法得逞了。

博奕遊戲的莊家

在博奕遊戲中,假設電腦是莊家,抽了一張牌叫玩家猜是哪張。玩家猜了是紅心A,結果莊家翻開後是黑桃三。這時玩家就會開始大吵大鬧說電腦作弊「一定是我先猜了,你才換牌秀給我看。」

那麼,把時間拉回遊戲前,如果在莊家抽牌之後,遊戲先用MD5.hash()取得這張牌的Hash字串,並公開給玩家看,再請玩家猜牌。等玩家猜完之後莊家翻牌,這時再比對一下這張牌的Hash和之前玩家看到的Hash是不是一樣,就可以確定莊家有沒有作弊了。


由以上的例子中,同學們是不是可以感覺得到,這個Hash還真的挺有前途的。Hash防碰撞以及不可逆的兩個特性,還能如何在遊戲裏發揮,就等著同學們在下次製作遊戲的過程裏露一手了。


上一篇
Trick 14: 為什麼要寫自己的亂數產生器
下一篇
Trick 16: 用MD5亂數產生器當個造物主
系列文
30個遊戲程設的錦囊妙計32
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

1 則留言

0
miku3920
iT邦新手 2 級 ‧ 2022-09-18 22:42:49

儲存 hash 防止篡改(?
聽起來很 JWT

小哈片刻 iT邦研究生 4 級 ‧ 2022-09-19 01:54:01 檢舉

簡直一模一樣~ /images/emoticon/emoticon39.gif

我要留言

立即登入留言