密碼學、Hash、編碼

愷開
愷開
Nov 6, 2017 · 8 min read

我們需要一個可靠的演算法對資料加密,就算駭客拿到資料也不知道內容,或者,就算駭客修改了資料內容傳到接收端後,也能夠被接受端發現資料已經遭到竄改。

或者,一般的應用中只要服務牽涉到會員登入的服務,可能就會遇到幾個問題:「如何處理使用者密碼?如何正確保存使用者的資料?」

最直覺的做法當然是直接把它存在資料庫。並且假設資料庫是安全的。

但很快會發現幾個問題:

  1. 管理者可以看到使用者的明文密碼,如果是有心人士,他們或許可以把你的密碼拿去其他網站試試看,這樣你的個資就全部洩露了
  2. 萬一不小心被駭客入侵,會員資料會全部都會被洩漏。

當然,對於使用者來說,他們並不會知道資料庫是否有將密碼加密,但是身為一個專業的工程師,如果對於資料的保存不夠謹慎,一旦曝光只會讓自己陷入苦境。

所以如何處理密碼儲存、如何幫使用者的資料加密,需要牽涉到一些密碼學的基本知識。 當然如果你使用像是 devise 之類成熟的 library,你可以完全忽略這些瑣事,但根據抽象化滲透法則,或許有一天你會需要這些知識也說不定。(像是當有人跟你說 base64 是種 hash 壓縮之類的話時,你有能力識別而不是跟著相信)

密碼學主要分類

根據用途不同,主要可以分為:

  • 編碼:其實不算加密,只是將資料編碼方便計算、或是在網路上傳遞。實際使用時很常看到,所以特別提出來。像是 UTF-8, ASCII, Unicode, base64 編碼等等。
  • Hash (單向雜湊):將長度不固定的資料映射為固定長度的字串,如 MD5, SHA。
  • 對稱性密碼系統:加密與解密時使用相同的密鑰。常見的演算法有 AES
  • 非對稱密碼系統:除了自己擁有密鑰(私鑰)之外,還有一組公鑰。經過公鑰加密的資料只有私鑰能夠解密、而用私鑰加密過後的資料只能用公鑰解密。著名的非對稱密碼 RSA 演算法

編碼

為了讓資料可以在網路間傳送,需要一種方式能夠對資料、字元做編碼,封包可能經由多個路由器傳送,而每個路由器的編碼設定可能不同,因此需要透過編碼讓資料能夠在網路間傳遞。

編碼並不算加密,因為加密需要鑰匙,而且需要持有鑰匙的人才能解密。

ASCII

早期的編碼是在美國發明使用,因此只需要將字元透過編碼轉為 2 進制的數字。通常用 1byte(8 bits) 來表示一個字元,因此 ASCII 除了 26 個大小寫英文外、數字,還包含常見的控制字元與標點符號,也就是常常在計概課出現的考題,像是 32 = 空白,65 = A 等等,總共定義了 128 個符號。

不過缺點顯而易見,因為只能傳送特定的字元,因此像是其它國家的語言,甚至像是漢字這種需要 2byte 表示的字元就沒辦法處理。所以目前通常使用 Unicode 來表示字元。其中最著名的實作為 UTF-8。

Base64

Base64 是一種透過可表示的字元來表示二進制資料的編碼方式。詳細的編碼方式可以參考維基百科。例如在網頁中最常見的,透過將小圖片編碼為 base64 的方式來減少請求數。

從實作中可以發現,base64 會比原始資料多 4/3 左右。這種編碼方式沒辦法加密資料,也沒辦法壓縮資料。

這可以當作判斷工程師的指標之一,如果他跟你說用 base64 可以用來加密與壓縮資料,那麼以後不要相信他說的話。

One way Hash — 單向雜湊

什麼是雜湊? 將資料經過設計好的 hash function,放入一個固定長度的 table 當中。Hash 其實也不算加密,因為良好的雜湊函數並不可逆,也沒有跟某個 key 進行運算的動作。

一個簡單的例子是透過 mod 運算,例如下面的範例:

雜湊後的值為34

這個簡單的 hash function 顯然有許多問題。

  1. 任何末二位數字是 34 的密碼,hash 值都一樣。例如:’11234', ‘44441234’。這樣一來儘管密碼不是原來的那份密碼(’1234'),也會有同樣的雜湊值。這樣的行為叫做衝突(collision)。關於 hash 中如何解決衝突有幾種方式。
  2. hash 值只有 0 ~ 99
  3. 可以從 hash function 推敲原始密碼。只要是 34 結尾都有可能是密碼

因此對於 Hash 來說,為了保持不可逆性,我們希望良好的 hash function 有幾個特性:

  • 不可逆:不能從雜湊值中推出原始的密碼是什麼
  • 不一樣的值雜湊後不應該相同
  • 設計一個不容易產生碰撞的 hash function,是實作單向雜湊的重點。

因此在現代的資料庫系統中,密碼通常是用 hash 過後的值保存。如果有任何教學直接將明碼存進資料庫,而且還沒有警告你這很不安全的話,不要再繼續往下看了。

一個完整的雜湊函式並不容易設計,目前著名的雜湊函式有 MD5, SHA1, SHA2,目前 MD5, SHA1 都已經被破解。

此外,還有一個問題要解決。雖然 hash 值不可逆,但駭客能夠預先生成常見明文與 hash 的對照表(又叫做彩虹表),如果找到 hash 能夠對應的明文就能夠破解密碼。

因此,比較好的做法是雜湊時加上一組亂數(通常被稱作 salt)一起加密。讓駭客沒辦法輕易透過預先生成的雜湊表來查詢明文。

小結

  • 良好的 hash function 在於它容不容易產生碰撞。(理論上所有 hash function 都會產生碰撞)
  • 目前 MD5, SHA1 都已經被淘汰,請使用比較安全的 SHA256, SHA512
  • 加入亂數 salt 來減少被明文完全洩漏的問題

更安全的雜湊:bcrypt

一般的 hash function 都被設計成在安全的情況下盡可能地加快計算時間,但如果你在實作密碼雜湊,更好的選擇可能是 bcrypt 或其他專門為雜湊密碼而設計的 hash function。 除了他的實作上讓密碼更安全之外(演算法本身已經幫你處理掉隨機 salt 的問題),且能夠透過 cost 函數提升複雜度。許多程式語言也已經實作相關的 library。

對稱式加密 — AES

對稱式加密需要雙方都持有同樣的 Key 才能夠加密、解密。目前最流行的演算法為 AES。主要是利用 XOR 的特性達成。

主要的運作原理是將明文拆成固定大小的區塊後分別進行加、解密,常見的工作模式有幾種

非對稱式加密 — RSA

RSA 是個數學味道濃厚且相當重要的加密演算法。他的破解困難度是建立在對一個合數分解為兩個大質數的困難性。因為因式分解兩個大質數的乘積相當困難(證明稍嫌複雜,需要有質因數分解、質數與同餘等概念)

所謂的非對稱式加密,是指加密與解密時可以使用不同的鑰匙(key)。這樣一來可以將公鑰給發送訊息端,而私鑰則持有在自己身上,並且透過私鑰來解密。這樣可以保證只有自己才能夠解密訊息。

2009 年 12 月 12 日,RSA-768(768 bits, 232 digits)也被成功分解了,目前比較安全的 key 長度為 RSA-1024 或 RSA-2048。

不過儘管非對稱式加密已經相當普遍,使用對稱式加密的好處在於效率。因為隨機找出兩個大質數需要耗費比較久的時間。

JWT(JSON Web Tokens)

JWT 透過輕巧的規範方便傳遞訊息,其實並不算密碼學的一部份,而是一種標準。最近因為 SPA 流行的原因,需要大量的前後端互動,許多驗證方式都開始使用 JWT,在這邊做介紹。

JWT 透過一個 header 與 payload(需要傳遞的資料)做 base64 編碼後再透過(HMAC 或 RSA)來加密產生簽名。

一個 JWT token 大概會長這樣:

優點在於透過這樣的 token,能夠包含以下資訊

  • header
  • payload
  • sign

這樣 token 被知道不就洩漏訊息了嗎?沒錯,只要將 header 與 payload 透過 base64 解碼就知道原始資料是什麼,但 token 本來就不該到處公開給別人使用才對,也因此 JWT 中不應該放入敏感資訊,例如使用者密碼。

至於要如何避免資料被竄改的可能?例如攻擊者把 payload 修改後再送出。這時伺服器端會檢查訊息跟加密後的字串是否與簽名相同。如果不同代表已經被竄改,而不知道 secret key 的情況下也就沒有辦法知道如何製作正確的簽名。

更詳細的介紹可以到 jwt.io 官方網站尋找更多資訊與實作。

小結

  • web service 做 authentication 時可以透過 JWT 當作 token 使用
  • 不要把密碼或任何敏感資料放入 JWT 傳送

結論

  • 編碼、Hash 並不是加密
  • SHA-1, MD5 已經被破解,使用更安全的 SHA-2
  • 實作密碼雜湊函式時,選擇更安全的 bcrypt 演算法。
  • 不要用 JWT 傳送敏感資料
  • 如果你的朋友笑著跟你說他們使用明碼儲存密碼,趕快跟他絕交。
  • 使用穩定的 library,盡量不要自幹加密演算法。一個穩固、安全的演算法是數學家嘔心瀝血的結晶,並不是三天兩頭就能生出來的。不過這並不代表不能夠探究密碼學中的原理。

密碼學對開發來說,看起來可能微不足道,在多數的情況下我們也不需要自己實作加密演算法。但如果不了解這些演算法的原理,我們很有可能挑選一個有破綻的加密演算法、將密碼暴露在極度危險的環境中。

另外就算是前端,在日常工作中,或許也有機會接觸到加密相關的需求。例如在前端加密後再送出(當然還是要在伺服器端再加密),或者私人訊息加密後再傳到 Server 端。

資安並不是一個碰到再說的 buzzword,而是一位具有專業素養的工程師都應該具備的基本常識。因此整理了密碼學的相關知識與資料。

每個加密演算法其背後都有一套蠻複雜的數學理論,有興趣的讀者們可以自行尋找這些演算法是如何實作,並且如何被證明是安全的。

愷開

Written by

愷開

https://blog.kalan.dev UNder-fitting Frontend Developer

De-Magazine

我們提供有關工程師與設計師的相關文章。

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade