一次搞懂密碼學中的三兄弟 — Encode、Encrypt 跟 Hash

Larry Lu
Larry Lu
Mar 23, 2020 · 10 min read

前言

隨著資訊安全越來越受到使用者的重視,密碼學作為資訊安全的基石也逐漸變成開發人員的必修課,所以筆者我今天就要來說說密碼學中大家很容易搞混的三個東西:編碼(Encode)、加密(Encrypt)跟雜湊(Hash)

雖然他們三者的比較已經很多人寫過了,但還是有些人搞不清楚,所以這篇決定換個方式:不提太多理論,而是舉大量的例子跟實際應用,如果這些例子你都能看懂,那自然就會知道三者的差別

編碼(Encoding)

首先來看看最簡單的編碼,所謂的編碼並不會修改資料、也沒有任何加密的效果,單純就是 換個方式來表達資料 而已,其中最有名的例子就是摩斯密碼

在摩斯密碼的編碼系統中,每個英文字母都可以被表示成點(dot)跟劃(dash)的組合,或是表示成長音跟短音,譬如說將 Hello World 編碼(encode)成摩斯密碼的形式就是 .... . .-.. .-.. --- .-- --- .-. .-.. -..

雖然這串東西乍看之下很難懂,但 只要知道摩斯密碼的轉換規則 就有辦法翻譯回來,所以編碼 完全沒有安全性可言,就只是 換個方式來表達資料 而已

實例

1. encodeURI() and decodeURI()

在 JavaScript 中有兩個很實用的 function 分別是 encodeURIdecodeURI,他們是用來把網址中的特殊字元(空白、標點符號等等)編碼成符合 URL 的格式(轉完變超醜的)

譬如說 https://www.google.com/search?q=創世神 這段網址會被編碼成 https://www.google.com/search?q=%E5%89%B5%E4%B8%96%E7%A5%9E,所以即便電腦不支援顯示中文,或是某些比較舊的瀏覽器無法貼中文網址,也還是可以透過編碼後的網址連到目標網站

2. Base64

Base64 是一種可以把二進位的資料編碼成 ASCII 字元的方法,如果你想在純文字的介面上傳送圖片給朋友,可以先用指令 cat hello.png | base64 把圖片編碼成 Base64 格式,然後把生出來的這一大串東西傳給他

你的朋友收到 這團看起來像是亂碼的東西 後,只要用 Base64 進行 decode 就可以收到原本的圖片了:echo {那一大串} | base64 -D > image.png

為了讓編碼後的那一大串能顯示在一個螢幕之內,我用的圖片畫質很差,請不要介意 XD

雖然這團東西看起來像亂碼,但他是沒有任何安全性的,熟悉 Base64 的人很快就能解碼回來,頂多只能騙騙你那不懂電腦的老媽XD

3. 霍夫曼編碼

霍夫曼編碼(Huffman Coding)是一種用來進行 無失真壓縮 的編碼演算法,說穿了他的概念就是把常用的字記成縮寫,從而降低資料量、達到壓縮的效果

如果還是覺得有點抽象的話,可以想想你去早餐店跟阿姨點餐時:

我要 “一個雞腿堡加蛋“ 跟 “大杯的冰紅茶”

因為阿姨腦中有一個早餐縮寫的對照表,所以在你講的同時阿姨就會快速在紙上記下 “G堡蛋、大冰紅”,這就是壓縮技術的原理:把常出現的字用縮寫記錄下來,等日後要解壓縮時再還原回去

加密(Encrypt)

加密跟剛說到的編碼有點像,唯一不同之處是加密跟解密必須要有金鑰(Key)才能進行。以最簡單的 凱薩加密法 來說,他加密的方式就是把每個英文字母加上一個 偏移量,這個偏移量就是用來執行加解密的 Key

假如我想要用凱薩加密法對 Hello World 進行加密,並且設定 key=3,那加密完的結果就會變成 Khoor Zruog,只有知道 key 的人才有辦法把 Khoor Zruoog 還原回去 Hello World

說是這麼說,但你我都知道英文字母不就那 26 個嗎?照目前電腦的運算速度,只要把偏移量從 0 到 25 都算過一遍,即便我不知道 key 也可以很容易就看出偏移量是 3

所以說凱薩加密法非常不安全,他最大的用處應該就是在課堂上傳紙條,但又不想被中間的同學看懂(我就幹過這種事XD),除此之外在現代已經沒什麼用處了

實例

1. AES

AES (Advanced Encryption Standard) 是一種對稱加密演算法,所謂的對稱就是說加密解密 都是用同一個 key,這點跟上面說到的凱薩加密法一樣,但 AES 不像凱薩的 key 只有 0–25 這麼少種,而是可以有超過 10³⁸ 種,所以安全性比凱薩高非常非常多

除了安全性高不易被破解之外,AES 加密檔案的速度也非常快,所以被美國政府用來加密機密檔案。如果你也想用 AES 來加密 D 槽裡面那些神秘檔案,可以試試系統內建的 OpenSSL

加密:openssl aes-256-cbc -in <input> -out <output>,輸入密碼(Key)加密後會變成一團醜不拉嘰的亂碼,完全不知道從何破解起,不像凱薩加密完還是英文字母,一下子就猜到了

要解密時就同樣的指令加個 -d,輕輕鬆鬆

但如果你不小心忘記密碼,那就真的 gg 了,因為 AES 加密後的檔案即便用超級電腦來破解,也要超過十億年,更不用說用你的電腦了XD

對稱式加密法的缺點

AES 又快又安全,聽起來超厲害對吧?但他這類 對稱式加密法 有一個缺點:就是如果你想把加密後的 文字/檔案 傳給你的秘密情人 Alice,兩個人就必須先講好密碼是什麼,這樣你傳過去她才用你設的密碼解密

但因為網路環境是不安全的(尤其是在有 HTTPS 之前),所以除非你把 Alice 當面約出來講密碼是什麼,不然只要是透過網路把密碼傳給對方,就有機會被中間人拿走

一旦中間人拿到密碼,那你用再強的加密演算法都沒有用。而且比起完全沒加密,更恐怖的是明明密碼已經被偷走了,你還自以為檔案有加密就超安全,真的怎麼死的都不知道

2. RSA

為了解決上述的問題,於是就有了非對稱加密法,其中最有名的就是 RSA

RSA 這類非對稱加密法有個很特別的地方,就是他會產生一組兩個 Key 分別叫公鑰(Public Key)跟私鑰(Private Key),而且 用公鑰加密的內容只能用私鑰解(超神奇的!!!)

同樣的情況當你要傳檔案給 Alice,就先請 Alice 生一組 Key 然後把公鑰傳給你。你有了 Alice 產生的公鑰之後,就用公鑰幫檔案加密再傳給她,Alice 收到就可以用她自己手上的私鑰解開(這段比較難,可以多看幾遍)

安全性方面,因為私鑰從頭到尾都在 Alice 手上,完全沒有傳出去過,所以即便中間人取得公鑰加密後的檔案也沒辦法解開,超安全 der

雜湊(Hashing)

接著是最後一個:雜湊,不知道大家有沒有注意過你的身分證字號,他的最後一碼其實是個驗證碼哦,他是前九碼根據 某個公式 計算出來的

M140051653 這個身分證字號來說,計算的方式是先把 M 轉換成對應的數字 21,接著根據下圖的方式進行計算,如果算出來剛好等於最後一碼,那代表這個身分證字號是合法的

這種把各個 欄位/字元 丟進去某個公式計算的方式就叫做雜湊(Hash),而這個計算公式就稱為 雜湊函數(Hash function),過程可能會做各種加減乘除,最後算出一個值或字串

因為最後一個數字 3 是經由前幾個數字計算、濃縮出來的,所以理所當然不可能由雜湊後的結果 3 回推出前幾個數字分別是什麼,所以雜湊的過程是 不可逆的

實例

1. 判斷檔案內容是否相同

如果你想驗證某兩個檔案的內容是否相同,可以使用 md5 來計算檔案的雜湊值,他會使用某個 hash function 根據檔案內容 計算出一個長度 128 bit 的雜湊值,若是兩個檔案內容不同就會計算出不同的 hash value

其實有很低的機率(1/2¹²⁸)會產生碰撞,造成不同檔案有相同的 hash value,但因為機率太小一般情況下可以忽略

如上圖,從雜湊值可以看出 data1 跟 data2 的雜湊值是相同的,也就代表他們的檔案內容有很高機率一樣,而 data2 跟 data3 的雜湊值不同,代表他們的內容一定不一樣

2. 驗證檔案是否被竄改

如果你開發過前端的話應該會發現,在你要使用 bootstrap 或 jquery 這類第三方 library 時,官方都會提供一個 integrity 屬性,那個就是 library 程式碼經過 SHA-2 演算法 hash 的結果

這樣子做有個好處,因為使用 bootstrap 的網站太多了,如果有天很不幸官方提供的 bootstrap.js 網址被駭客偷換成惡意程式碼,就會有很多網站遭殃

但有了 hash 之後,因為不同的程式碼會導致不同的 hash value,所以瀏覽器看到就不會載入怪怪的 bootstrap.js,只因為他的 hash 結果跟原本的不一樣

3. 驗證使用者密碼

大部分網站的後端資料庫都不會直接儲存使用者的密碼,因為那樣太危險了,取而代之的是儲存密碼的雜湊值

當使用者嘗試要登入時,只要將使用者輸入的密碼進行雜湊,再跟資料庫裡面的雜湊值比對就可以了。只要輸入的密碼是正確的,用同一個公式進行計算必定會得到相同的結果

安全性方面,因為雜湊值無法反推回原密碼,所以即便有一天資料庫不小心洩漏出去了,駭客還是拿不到使用者的密碼,只能拿到密碼的雜湊值

關於如何安全的儲存使用者密碼,可以看我另一篇「聽說不能用明文存密碼,那到底該怎麼存?」會有更詳細的解說

重點整理

看到這相信大家都已經累了,怕大家看到後面就忘了前面,所以再條列一下他們三個的重點

編碼(Encoding)

  • 只是換個方式表達資料
  • 不需要 Key 即可解碼(不安全)

加密(Encrypt)

  • 用 Key 來保護資料的機密性
  • 加密跟解密都需要 Key

雜湊(Hashing)

  • 把資料丟進一串公式計算出一個結果
  • 無法反推回原字串

總結

相信大家都了解編碼、加密跟雜湊的不同了,因為他們三者各有優缺,適用的情境也不太一樣,所以實務上常常各取所長把他們混在一起用

譬如說壓縮檔加密就同時用到編碼跟加密、JWT(JSON Web Token)用到編碼跟雜湊、HTTPS 的實現則是用到加密跟雜湊…

像這樣的例子還有很多,他們三者相輔相成才有了現在流行的各種認證機制,很多聽起來很神奇、很安全的功能其實背後都是他們在撐,所以說他們是網路安全的基石也不為過吧!

延伸閱讀

Starbugs Weekly 星巴哥技術專欄

一群技術人想要寫出一些好文章所建立的技術專欄。每週二一篇原創文章、一封電子報,歡迎大家訂閱!主網站: https://weekly.starbugs.dev/。

Starbugs Weekly 星巴哥技術專欄

一群技術人想要寫出一些好文章所建立的技術專欄。每週二一篇原創文章、一封電子報,歡迎大家訂閱!主網站: https://weekly.starbugs.dev/。

Larry Lu

Written by

Larry Lu

我是 Larry 盧承億,傳說中的 0.1 倍工程師。我熱愛技術、喜歡與人分享,專長是 Javascript 跟 Go,平常會寫寫技術文章還有參加各種技術活動,歡迎大家來找我聊聊技術~

Starbugs Weekly 星巴哥技術專欄

一群技術人想要寫出一些好文章所建立的技術專欄。每週二一篇原創文章、一封電子報,歡迎大家訂閱!主網站: https://weekly.starbugs.dev/。