Base64? SHA? MD5? AES? 你搞得我好亂啊
曾經在工作時,有一位同事說在面試新人時,有位求職者在回答設計登入機制時自己會對敏感資訊用 Base64 加密並存到資料庫裡等等等。我不禁問:「什麼時候 Base64 是一種加密演算法了?」周圍的同事聊起來,發現大家對於加密、雜湊這些名詞的理解都不太一樣,甚至也有一些誤解。所以今天就來聊聊到底什麼是加密、什麼是雜湊,還有 Base64 究竟是什麼東西吧!

編碼(Encoding)
前面提到的 Base64,其實是一種「編碼」(Encoding)方式,所謂編碼,就是將資料從一種格式轉換成另一種格式的過程。所以它並不具備驗證資料來源與確定資料完整性的功能。單純只是將資料轉換成另一種格式,讓資料能夠在不同系統或應用程式之間進行傳輸或儲存。
Base64 編碼的主要目的是將二進位資料轉換成 ASCII 字元,這樣就可以在只能處理文字的系統中傳輸二進位資料了。像是網站的網址就可以用 base64 編碼來傳輸圖片、檔案等二進位資料。就跟其他的編碼系統的用途比如說 URL 編碼(URL Encoding)、HTML 編碼(HTML Encoding)、Unicode 編碼(Unicode Encoding)等等是一樣的。
INFO
之所以很多人會把 base64 誤以為是加密演算法,是因為它可以將原本看起來像是一堆亂碼的二進位資料,轉換成一串可讀的 ASCII 字元,讓人看起來像是被「加密」了一樣,但不是看起來像亂碼就是加密喔!
原始文字 Hello, World!
base64格式 SGVsbG8sIFdvcmxkIQ==
MD5雜湊值 65a8e27d887d4a598b5f3d9f0f6f1e6c
雜湊(Hashing)
雜湊,是一種單向式的資料轉換方式。它會將任意長度的輸入資料,經過一個演算法後,轉換成固定長度的輸出資料,這個輸出資料通常稱為「雜湊值」或「資料摘要」。常見的雜湊演算法有 MD5、SHA-1、SHA-2(SHA-128, SHA-256, SHA-512), SHA3 等等。
所以雜湊很常用於密碼儲存、資料完整性驗證、數位簽章等需要驗證資料的真實性還有完整性的場合。像是登入機制就會先將使用者輸入的帳號向資料庫查詢密碼雜湊值,接著將使用者輸入的密碼進行雜湊處理,最後比對兩個雜湊值是否相同來驗證密碼是否正確。
又或者是下載檔案時,網站會提供檔案的雜湊值讓使用者下載後可以驗證檔案是否在傳輸過程中被竄改過,來保護使用者下載到的檔案是完整且正確的。
雜湊的特性包括:
- 單向性:雜湊過程是不可逆的,無法從雜湊值還原回原始資料。像是果汁機或是絞肉機一樣,將資料丟進去打碎之後就無法還原回原本的樣子了。
- 固定長度輸出:無論輸入資料的長度是多少,雜湊值的長度都是固定的。例如,SHA-256 的輸出長度總是 256 位元(32 字節)。
- 敏感性:即使輸入資料有微小的變化,雜湊值也會產生巨大的變化。這意味著即使只改變一個字元,雜湊值也會完全不同。
- 抗碰撞性:理論上,找到兩個不同的輸入資料產生相同的雜湊值是非常困難的。(這裡講的是理論上,事實上雜湊值是會出現碰撞的)
- 快速計算:雜湊演算法通常設計得非常高效,能夠快速地計算出雜湊值。
// 使用 MD5 雜湊一段文字
const crypto = require('crypto');
const hash = crypto.createHash('md5').update('Hello, World!').digest('hex');
console.log(hash); // 輸出: 65a8e27d887d4a598b5f3d9f0f6f1e6c雜湊的弱點
雜湊雖然可以適用於驗證資料完整性,也兼具速度快的優勢,但這也成為它的弱點。像是 資料長度只有 128 bit 的 MD5、160 bit 的 SHA-1,在面對現代各式各樣的資料長度,已經被證實在不同的資料下會出現一樣的雜湊值,也就是所謂的「碰撞」(Collision)。這代表著攻擊者可以找到兩組不同的資料,卻產生相同的雜湊值,進而繞過資料完整性驗證。
由於雜湊演算法的執行速度很快,在面對 MD5, SHA-1 這些長度不足的雜湊演算法時,攻擊者可以高效的快速嘗試大量不同的輸入資料,來尋找碰撞,這種攻擊方式稱為「暴力破解」(Brute Force Attack)。以 SHA-1 為例,用目前現代 GPU 的運算能力,大約只需要幾小時到幾分鐘甚至幾秒鐘的時間就能破解出碰撞,從而實現破解密碼、偽造數位簽章等攻擊行為。
而另外一個常見的攻擊方式是「彩虹表攻擊」(Rainbow Table Attack)。攻擊者事先計算出大量常見密碼的雜湊值(像是abc123, p@$$w0rd, 1qaz@WSX, qwerasdfzxcv 這類偷懶型密碼),並將這些密碼與雜湊值存儲在一個稱為「彩虹表」的鍵值資料結構中。當攻擊者獲取到某個密碼的雜湊值時,可以快速查詢彩虹表來找到對應的原始密碼,從而破解密碼。
INFO
注意,彩虹表攻擊並不是針對雜湊演算法本身的弱點,而是利用了使用者習慣使用弱密碼的行為來進行攻擊。所以即使是強大的雜湊演算法,如果使用者選擇了弱密碼,仍然有可能被彩虹表攻擊破解。所以養成使用強密碼的習慣是非常重要的。
所以為了因應這些攻擊方式,現代的雜湊演算法通常會採取
- 更長的輸出長度(如 SHA-256, SHA-512)
- 更慢的執行速度 (如 bcrypt, scrypt, Argon2)
- 更多次的雜湊迭代(如 PBKDF2)
- 雜湊加料 來增強雜湊的安全性。
湯不夠有味道,加點鹽跟胡椒(Salting 與 Peppering)
為了進一步提升雜湊的安全性,通常會在雜湊前加入一個隨機值(通常被稱為「鹽值」(Salt)),來讓密碼在雜湊前變得更複雜,從而增加破解的難度、也減少同個系統內不同使用者的密碼碰撞。
比如說,有兩個使用者的密碼都是 "password123"。如果直接對這兩個密碼進行雜湊,會得到相同的雜湊值,這樣攻擊者就可以輕易地識別出這兩個使用者使用了相同的密碼。但是如果我們在雜湊前為每個使用者加入不同的鹽值,例如:
- 使用者 A 的鹽值是 "randomSaltA"
- 使用者 B 的鹽值是 "randomSaltB"
那麼在雜湊前,我們會將密碼與鹽值結合起來:
- 使用者 A 的雜湊輸入是 "password123randomSaltA"
- 使用者 B 的雜湊輸入是 "password123randomSaltB"
這樣即使兩個使用者的密碼相同,最終產生的雜湊值也會完全不同,大大增加了攻擊者破解密碼的難度。
但是鹽值通常在產生之後也要連同雜湊值一起儲存起來,否則下一次使用者登入就會無法驗證密碼了。所以假設資料庫被攻擊者入侵,攻擊者仍然可以取得鹽值與雜湊值,進而利用彩虹表攻擊來破解密碼。
因此,還有另一種做法是使用「胡椒值」(Pepper)。胡椒值是一個全系統共用的秘密值,通常不會存儲在資料庫中,而是以 .env 或是 .config 這類配置檔案的形式存在伺服器上讓程式讀取。這樣即使資料庫被攻擊者入侵,攻擊者也無法取得胡椒值,進而增加破解密碼的難度。
值得一提的是,像是 bcrypt, scrypt, Argon2 這些現代的密碼雜湊演算法,通常已經內建了鹽值的機制,所以在使用這些演算法時,不需要額外手動加入鹽值,只需要在進入雜湊前加入胡椒值即可。
// 示範使用 bcrypt 進行帶胡椒值的密碼雜湊
// 讀取 .env 檔案中的胡椒值(.env 是一個環境變數檔案)
const bcrypt = require('bcrypt');
const pepper = process.env.PEPPER;
const saltRounds = 10; // bcrypt 內建的鹽值機制
async function hashPassword(password) {
// 在密碼前加入胡椒值
const passwordWithPepper = password + pepper;
// 使用 bcrypt 進行雜湊
const hashedPassword = await bcrypt.hash(passwordWithPepper, saltRounds);
return hashedPassword;
}
// 使用範例
const hashedPwd = await hashPassword('abc123')加密(Encryption)
加密這件事有點像是你今天跟嘉南羊乳訂了羊奶,每天早上送羊奶的人都會用鑰匙打開羊奶箱、將商品放到你的門口的箱子裡鎖起來,只有你有鑰匙可以打開箱子拿到羊奶、知道今天的羊奶是什麼口味的。即使有人看到你門口有掛羊奶箱、也看到送乳人放東西進去,也無法直接拿走、知道裡面是什麼,因為箱子是鎖著的。
而描述這個過程的演算法就叫做「加密演算法」,常見的有對稱式加密(Symmetric Encryption)和非對稱式加密(Asymmetric Encryption)。
對稱式加密(Symmetric Encryption)

對稱式加密是指加密和解密使用同一把鑰匙的加密方式。這就像是你和送乳人共用同一把鑰匙來打開羊奶箱一樣。常見的對稱式加密演算法有 AES(Advanced Encryption Standard)、DES(Data Encryption Standard)等。
對稱式加密的優點是加密和解密速度快,適合用於大量資料的加密,常用於硬碟加密、資料庫加密、串流媒體加密等場合。但對稱式加密的缺點是鑰匙的管理比較麻煩,每兩個通訊方都需要有一把共用的鑰匙,所以假設有一百個人要互相通訊,就需要有
// 使用 AES 對稱式加密一段文字
const crypto = require('crypto');
const algorithm = 'aes-256-cbc';
const key = crypto.randomBytes(32); // 32 bytes for aes-256
const iv = crypto.randomBytes(16); // Initialization vector
function encrypt(text) {
const cipher = crypto.createCipheriv(algorithm, key, iv);
let encrypted = cipher.update(text, 'utf8', 'hex');
encrypted += cipher.final('hex');
return encrypted;
}
function decrypt(encrypted) {
const decipher = crypto.createDecipheriv(algorithm, key, iv);
let decrypted = decipher.update(encrypted, 'hex', 'utf8');
decrypted += decipher.final('utf8');
return decrypted;
}
const encryptedText = encrypt('Hello, World!');
console.log('Encrypted:', encryptedText);
const decryptedText = decrypt(encryptedText);
console.log('Decrypted:', decryptedText); // 輸出: Hello, World!非對稱式加密(Asymmetric Encryption)

以前面的送羊奶為例,假設羊奶箱有兩個鎖頭,一個是拿來讓人打開投遞孔的,而且這把鑰匙掛在箱子上,誰都能夠把投遞口打開、把東西丟進去。另一個是取物用的鎖頭,但只有你有取物口的鑰匙可以拿到裡面的東西,所以就算送奶人丟錯奶了,他也不能打開取物口拿走東西,因為他沒有鑰匙;哪天若是有人質疑這個羊奶箱不是你的,只要在大家面前打開取物口,就可以用手上的鑰匙證明這個箱子是你的,因為只有你有這把鑰匙。
像這樣用不同的鑰匙來管理投遞跟取物的方式就稱為「非對稱式」。而像這樣用不同的鑰匙處理加密和解密的加密方式,就叫做「非對稱式加密」。常見的非對稱式加密演算法有 RSA(Rivest-Shamir-Adleman)、ECC(Elliptic Curve Cryptography)等。
非對稱式加密的優點是鑰匙管理比較簡單,因為每個人只需要一把私鑰(用於解密)和一把公鑰(用於加密),公鑰可以發給任何人,用來將資料加密、但無法用公鑰解密,只有持有私鑰的人可以解密資料。所以即使公鑰被竊取,也不會影響資料的安全性。非對稱式加密也可以用於數位簽章,以私鑰對資料進行簽署,任何人都可以用公鑰來驗證簽章的真實性,確保資料沒有被竄改過。
但非對稱式加密的缺點是加密和解密速度較慢,僅適合用於少量資料的加密,通常用於金鑰交換、數位簽章等場合。
// 使用 RSA 非對稱式加密一段文字
const crypto = require('crypto');
const { publicKey, privateKey } = crypto.generateKeyPairSync('rsa', {
modulusLength: 2048,
});
function encrypt(text) {
return crypto.publicEncrypt(
{
key: publicKey,
padding: crypto.constants.RSA_PKCS1_OAEP_PADDING,
oaepHash: 'sha256',
},
Buffer.from(text)
).toString('base64');
}
function decrypt(encrypted) {
return crypto.privateDecrypt(
{
key: privateKey,
padding: crypto.constants.RSA_PKCS1_OAEP_PADDING,
oaepHash: 'sha256',
},
Buffer.from(encrypted, 'base64')
).toString('utf8');
}
// 用公鑰加密
const encryptedText = encrypt('Hello, World!');
console.log('Encrypted:', encryptedText);
// 用私鑰解密
const decryptedText = decrypt(encryptedText);
console.log('Decrypted:', decryptedText); // 輸出: Hello, World!// 使用 RSA 進行數位簽章
const crypto = require('crypto');
const { publicKey, privateKey } = crypto.generateKeyPairSync('rsa', {
modulusLength: 2048,
});
function sign(data) {
const sign = crypto.createSign('SHA256');
sign.update(data);
sign.end();
return sign.sign(privateKey, 'hex');
}
function verify(data, signature) {
const verify = crypto.createVerify('SHA256');
verify.update(data);
verify.end();
return verify.verify(publicKey, signature, 'hex');
}
// 用私鑰簽章
const data = 'Important message';
const signature = sign(data);
console.log('Signature:', signature);
// 用公鑰驗證簽章
const isValid = verify(data, signature);
console.log('Is signature valid?', isValid); // 輸出: true混合式加密(Hybrid Encryption)
由於對稱式加密需要高安全性的鑰匙管理,但具備高效率;而非對稱式加密則解決了鑰匙管理的問題,但效率較低。因此在實際應用中,通常會將兩者結合起來使用,稱為「混合式加密」。實際用法是先用非對稱式加密來驗證彼此的身份,再用加密的方式交換一把對稱式加密的鑰匙,接著用這把對稱式加密的鑰匙來加密大量的資料。這樣就能同時兼顧安全性和效率。
範例:HTTPS 通訊協定(Hybrid Encryption)

第一階段 (網站事前準備)
- 網站伺服器產生一組公私鑰對
- 將網站公鑰和網站資訊送到受信任的第三方機構 CA 產生憑證,憑證內容包含網站的公鑰、基本資料,以及數位簽章
- 數位簽章是用網站的公鑰+網站資訊經過雜湊後再用 CA 的私鑰簽署而成的
第二階段 (瀏覽器連線網站)
- 瀏覽器連線到網站伺服器,伺服器將公鑰和憑證一起發送給瀏覽器
- 瀏覽器使用 CA 的公鑰來驗證憑證上的數位簽章(代表網站是經過 CA 驗證合法的)
- 瀏覽器生成一個隨機的對稱式加密鑰匙(會話鑰匙),並使用伺服器的公鑰將這個會話鑰匙加密後發送給伺服器
- 伺服器使用自己的私鑰解密出會話鑰匙
- 雙方使用這把會話鑰匙進行對稱式加密的通訊
總結

今天我們介紹了編碼、雜湊、加密,並且說明了它們之間的差異與應用場景,包括 Base64 編碼、MD5 雜湊、AES 對稱式加密、RSA 非對稱式加密,以及混合式加密在 HTTPS 通訊協定中的應用。其實這些技術在資訊安全中扮演著非常重要的角色,理解它們的原理和應用,有助於我們更好地保護資料的安全性和完整性。希望今天的介紹能夠幫助大家釐清這些概念,並在未來的工作或學習中能夠更有效地運用這些技術來保障資訊安全。
而在現今的數位時代,加密與雜湊技術除了保護資料安全之外,也逐漸轉變成金融經濟的主要技術,像是區塊鏈、加密貨幣等應用,都大量使用了加密與雜湊技術來確保交易的安全性與不可竄改性。未來隨著科技的進步,這些技術將會變得更加重要,成為我們日常生活中不可或缺的一部分。
