我們需要一個可靠的演算法對資料加密,就算駭客拿到資料也不知道內容,或者,就算駭客修改了資料內容傳到接收端後,也能夠被接受端發現資料已經遭到竄改。
或者,一般的應用中只要服務牽涉到會員登入的服務,可能就會遇到幾個問題:「如何處理使用者密碼?如何正確保存使用者的資料?」
最直覺的做法當然是直接把它存在資料庫。並且假設資料庫是安全的。
但很快會發現幾個問題:
當然,對於使用者來說,他們並不會知道資料庫是否有將密碼加密,但是身為一個專業的工程師,如果對於資料的保存不夠謹慎,一旦曝光只會讓自己陷入苦境。
根據用途不同,主要可以分為:
為了讓資料可以在網路間傳送,需要一種方式能夠對資料、字元做編碼,封包可能經由多個路由器傳送,而每個路由器的編碼設定可能不同,因此需要透過編碼讓資料能夠在網路間傳遞。
編碼並不算加密,因為加密需要鑰匙,而且需要持有鑰匙的人才能解密;但編碼只要按照規範的演算法執行就可以知道原始訊息是什麼。
早期的編碼是在美國發明使用,因此只需要將字元透過編碼轉為 2 進制的數字。通常用 1byte(8 bits) 來表示一個字元,因此 ASCII 除了 26 個大小寫英文外、數字,還包含常見的控制字元與標點符號,也就是常常在計概課出現的考題,像是 32 = 空白,65 = A 等等,總共定義了 128 個符號。
不過缺點顯而易見,因為只能傳送特定的字元,因此像是其它國家的語言,甚至像是漢字這種需要 2byte 表示的字元就沒辦法處理。所以目前通常使用 Unicode 來表示字元。其中最著名的實作為 UTF-8。
Base64 是一種透過可表示的字元來表示二進制資料的編碼方式。詳細的編碼方式可以參考維基百科。例如在網頁中最常見的,透過將小圖片編碼為 base64 的方式來減少請求數。
從實作中可以發現,base64 會比原始資料多 4/3 左右。這種編碼方式沒辦法加密資料,也沒辦法壓縮資料。
這可以當作判斷工程師的指標之一,如果他跟你說用 base64 可以用來加密與壓縮資料,那麼以後不要相信他說的話。
什麼是雜湊? 將資料經過設計好的 hash function,放入一個固定長度的 table 當中。Hash 其實也不算加密,因為良好的雜湊函數並不可逆,也沒有跟某個 key 進行運算的動作。
一個簡單的例子是透過 mod
運算,例如下面的範例:
const mypassword = '1234';
const myHashFn = (password) => password % 100
這個簡單的 hash function 顯然有許多問題。
因此對於 Hash 來說,為了保持不可逆性,我們希望良好的 hash function 有幾個特性:
因此在現代的資料庫系統中,密碼通常是用 hash 過後的值保存。如果有任何教學直接將明碼存進資料庫,而且還沒有警告你這很不安全的話,不要再繼續往下看了。
一個完整的雜湊函式並不容易設計,目前著名的雜湊函式有 MD5, SHA1, SHA2,目前 MD5, SHA1 都已經被破解。
此外,還有一個問題要解決。雖然 hash 值不可逆,但駭客能夠預先生成常見明文與 hash 的對照表(又叫做彩虹表),如果找到 hash 能夠對應的明文就能夠破解密碼。
因此,比較好的做法是雜湊時加上一組亂數(通常被稱作 salt)一起加密。讓駭客沒辦法輕易透過預先生成的雜湊表來查詢明文。
一般的 hash function 都被設計成在安全的情況下盡可能地加快計算時間,但如果你在實作密碼雜湊,更好的選擇可能是 bcrypt 或其他專門為雜湊密碼而設計的 hash function。 除了他的實作上讓密碼更安全之外(演算法本身已經幫你處理掉隨機 salt 的問題),且能夠透過 cost 函數提升複雜度。許多程式語言也已經實作相關的 library。
對稱式加密需要雙方都持有同樣的 Key 才能夠加密、解密。目前最流行的演算法為 AES。主要是利用 XOR 的特性達成。
主要的運作原理是將明文拆成固定大小的區塊後分別進行加、解密,常見的工作模式有幾種。
RSA 是個數學味道濃厚且相當重要的加密演算法。他的破解困難度是建立在對一個合數分解為兩個大質數的困難性。因為因式分解兩個大質數的乘積相當困難(證明稍嫌複雜,需要有質因數分解、質數與同餘等概念)
所謂的非對稱式加密,是指加密與解密時可以使用不同的鑰匙(key)。這樣一來可以將公鑰給發送訊息端,而私鑰則持有在自己身上,並且透過私鑰來解密。這樣可以保證只有自己才能夠解密訊息。
2009 年 12 月 12 日,RSA-768(768 bits, 232 digits)也被成功分解了,目前比較安全的 key 長度為 RSA-1024 或 RSA-2048。
不過儘管非對稱式加密已經相當普遍,使用對稱式加密的好處在於效率。因為隨機找出兩個大質數需要耗費比較久的時間。
JWT 透過輕巧的規範方便傳遞訊息,其實並不算密碼學的一部份,而是一種標準。最近因為 SPA 流行的原因,需要大量的前後端互動,許多驗證方式都開始使用 JWT,在這邊做介紹。
JWT 透過一個 header 與 payload(需要傳遞的資料)做 base64 編碼後再透過(HMAC 或 RSA)來加密產生簽名。
優點在於透過這樣的 token,能夠包含以下資訊
這樣 token 被知道不就洩漏訊息了嗎?沒錯,只要將 header 與 payload 透過 base64 解碼就知道原始資料是什麼,但 token 本來就不該到處公開給別人使用才對,也因此 JWT 中不應該放入敏感資訊,例如使用者密碼。
至於要如何避免資料被竄改的可能?例如攻擊者把 payload 修改後再送出。這時伺服器端會檢查訊息跟加密後的字串是否與簽名相同。如果不同代表已經被竄改,而不知道 secret key 的情況下也就沒有辦法知道如何製作正確的簽名。
更詳細的介紹可以到 jwt.io 官方網站尋找更多資訊與實作。
密碼學對開發來說,看起來可能微不足道,在多數的情況下我們也不需要自己實作加密演算法。但如果不了解這些演算法的原理,我們很有可能挑選一個有破綻的加密演算法、將密碼暴露在極度危險的環境中。
另外就算是前端,在日常工作中,或許也有機會接觸到加密相關的需求。例如在前端加密後再送出(當然還是要在伺服器端再加密),或者私人訊息加密後再傳到 Server 端。
資安並不是一個碰到再說的 buzzword,而是一位具有專業素養的工程師都應該具備的基本常識。因此整理了密碼學的相關知識與資料。