一次搞懂密碼學中的三兄弟 — Encode、Encrypt 跟 Hash
前言
隨著資訊安全越來越受到使用者的重視,密碼學作為資訊安全的基石也逐漸變成開發人員的必修課,所以筆者我今天就要來說說密碼學中大家很容易搞混的三個東西:編碼(Encode)、加密(Encrypt)跟雜湊(Hash)
雖然他們三者的比較已經很多人寫過了,但還是有些人搞不清楚,所以這篇決定換個方式:不提太多理論,而是舉大量的例子跟實際應用,如果這些例子你都能看懂,那自然就會知道三者的差別
編碼(Encoding)
首先來看看最簡單的編碼,所謂的編碼並不會修改資料、也沒有任何加密的效果,單純就是 換個方式來表達資料 而已,其中最有名的例子就是摩斯密碼
在摩斯密碼的編碼系統中,每個英文字母都可以被表示成點(dot)跟劃(dash)的組合,或是表示成長音跟短音,譬如說將 Hello World 編碼(encode)成摩斯密碼的形式就是 .... . .-.. .-.. --- .-- --- .-. .-.. -..
雖然這串東西乍看之下很難懂,但 只要知道摩斯密碼的轉換規則 就有辦法翻譯回來,所以編碼 完全沒有安全性可言,就只是 換個方式來表達資料 而已
實例
1. encodeURI() and decodeURI()
在 JavaScript 中有兩個很實用的 function 分別是 encodeURI
跟 decodeURI
,他們是用來把網址中的特殊字元(空白、標點符號等等)編碼成符合 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 的實現則是用到加密跟雜湊…
像這樣的例子還有很多,他們三者相輔相成才有了現在流行的各種認證機制,很多聽起來很神奇、很安全的功能其實背後都是他們在撐,所以說他們是網路安全的基石也不為過吧!