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

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

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

但很快會發現幾個問題:

  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 運算,例如下面的範例:

https://gist.github.com/f5a5e848c288aa6d62bf1f44c8051a17

雜湊後的值為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 大概會長這樣:

https://gist.github.com/8a53865883976dcab5c593b4a4ff98a4

優點在於透過這樣的 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,而是一位具有專業素養的工程師都應該具備的基本常識。因此整理了密碼學的相關知識與資料。

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

發表迴響

在下方填入你的資料或按右方圖示以社群網站登入:

WordPress.com 標誌

您的留言將使用 WordPress.com 帳號。 登出 /  變更 )

Google photo

您的留言將使用 Google 帳號。 登出 /  變更 )

Twitter picture

您的留言將使用 Twitter 帳號。 登出 /  變更 )

Facebook照片

您的留言將使用 Facebook 帳號。 登出 /  變更 )

連結到 %s