2013/02/20

Hash or Encrypt?

這天在公司思考一個問題...

假設每台機器都有編號,而且是以流水號的方式編排 0001, 0002, 0003 ...
要為每台機器預留一個 publish URL 像是 http://anything.com/0001/data,但又不想輕易讓其他人知道規則,例如 user 0001 可以用 http://anything.com/0002/data 拿到別人的 data。


方法一:Hash

第一想到的是把 id 經 MD5 hash,但 hash 就是有機會 collision,雖然這個機率比被雷打到還低很多,但機率這種東西就是有可能發生嘛!於是我就把所有的編號都拿去做 MD5,最後證明了沒有 collision 的問題。
MD5("0001") = 25bbdcd06c32d477f7fa1c3e4a91b032
http://anything.com/25bbdcd06c32d477f7fa1c3e4a91b032/data
是不是找不出其規則了

但又產生了另一個問題,短字串的 hash 結果很容易被彩虹表 (Rainbow table) 破解,意指先把所有 hash 結果計算出來,利用查表的方式反推原始字串,所以會有加 salt 的概念,也就是把原始字串先"處理"一下再去 hash,可能是額外加一些字串,或是反轉之類的,長度增加了可以降低被反推的機會,即使被反推回來也很難獲得原始字串。
SALT("0001") = 0a0b0c1d
MD5("0a0b0c1d") = b906c4ea093173e4c673ab6540696f0c
http://anything.com/b906c4ea093173e4c673ab6540696f0c/data
這樣子有比較複雜了吧!
salt 的方式有很多種,例如 MD5 兩次,或用其他 hash function,但要記得不能有兩個字串經過 salt 的結果是一樣的。


方法二:Encrypt

由於原始字串經過很大的變化,所以我也沒辦法驗證 collision 發生的可能性,後來決定還是改用加密法,加密法是 1 對 1 函數,沒有 collision 的問題。
DES(key: "god", plaintext: "0001") = d4f96d5da8bd0e6e
http://anything.com/d4f96d5da8bd0e6e/data

過程中比較棘手的是加密法的 變數實在太多,以 DES 來說,我用 openssl crypto library 時,要輸入 mode (cbc, ebc, cfb...)、input 又有分 64bit/128bit、是否加 padding,當想用 其他方式(pyDesOnline encrypt tool)來驗證時,竟然湊不出一樣的 ><


後記:

其實我需要的應該是一種只有自己知道的 encode/decode 方式,而且必須要是 id 加減1時,編碼結果就會變動很大,避免有心人士反推。用 encryption 是有點大材小用,但至少是 well known 的,所以各種語言都很容易找到 library,不需要自己 implement。


順便復習一下
  • Encode/Decode
    資料傳遞時的一種表達方式
    base64 encode, url encode ...
  • Encrypt/Decrypt
    以密文的方式傳遞資料,只有知道 key 的人可以解開得到明文,又分成對稱加密與非對稱加密
    AES, DES, Blowfish ...
  • Hash
    驗證資料完整性
    MD5, SHA1, CRC32


沒有留言:

張貼留言