← 全部文章

Base64 不是加密:開發者常見的誤解

Base64 編碼看起來像亂碼,但任何人一行函式就能解開。學習 Base64 到底是什麼、為何常被誤認為加密,以及真正需要保護資料時該用什麼。

「我把它 Base64 編碼一下,這樣就沒人能讀了。」這句話出現在程式碼審查、Slack 訊息以及 Stack Overflow 答案中的頻率,比它應該的高得多。Base64 不是加密。它不是混淆。它不是任何形式的安全機制。把它解碼回來只需要一次函式呼叫,且不需要任何金鑰或秘密。本文講清楚 Base64 到底是什麼、誤解為何根深柢固,以及當你真的需要保護資料時該用什麼。

Base64 實際是什麼

Base64 是一種編碼方案 —— 二進位資料與 64 個可列印 ASCII 字元(A–Za–z0–9+/,以及 = 作為填補)之間的可逆對映。

每 3 位元組的輸入會變成 4 個 Base64 字元。演算法完全是決定性的,不需要任何金鑰:

// 編碼
btoa("hello")          // → "aGVsbG8="
btoa("secret password") // → "c2VjcmV0IHBhc3N3b3Jk"

// 解碼 —— 一次呼叫,不需要金鑰
atob("c2VjcmV0IHBhc3N3b3Jk") // → "secret password"

就這樣。拿到編碼字串的任何人都能立刻解碼,在任何語言、任何環境中,不需要任何額外資訊。

誤解為何存在

Base64 編碼後的字串看起來像加密後的資料。它們由大小寫字母、數字和特殊字元混合而成,與原始輸入毫無可見相似之處。這種視覺雜訊足以矇騙一個非技術觀察者 —— 而出乎意料的是,常常也能矇騙技術人員。

這個模式還會被「位置鄰近」所強化。Base64 在安全相關的場合中到處都是:

  • HTTPS 憑證是 Base64 編碼的(PEM 格式)
  • JWT token 的 header 與 payload 使用 Base64url 編碼
  • HTTP Basic 認證以 Base64 傳送憑證
  • SSH 金鑰以 Base64 儲存
  • 加密後的密文經常以 Base64 作為傳輸形式

在這些與安全相關的位置反覆見到 Base64,開發者就把編碼與安全混為一談。編碼只是外包裝;安全來自底層的密碼學操作。

編碼 vs 加密:核心差別

差別簡單且絕對:

編碼(Base64)加密(AES、RSA…)
目的把二進位資料表示為文字對未授權方隱藏資料
需要金鑰?
任何人都能反向?能 —— 一次函式呼叫只有掌握正確金鑰才能
提供機密性?是(正確使用時)
輸出大小比輸入大約多 33%因演算法而異

加密用金鑰變換資料,使得在沒有金鑰的情況下還原原文在計算上不可行。Base64 編碼沒有這樣的屬性。

現實世界的安全錯誤

把「編碼過的」秘密放在原始碼裡

// ❌ 這並不秘密 —— 任何人都能解碼
const API_KEY = atob("c2stbGl2ZS1hYmMxMjM0NTY3ODk=");

// 任何讀到這段程式碼的人都會跑:
atob("c2stbGl2ZS1hYmMxMjM0NTY3ODk=") // → "sk-live-abc123456789"

自動化的金鑰掃描工具(GitHub 的 secret scanning、GitGuardian、truffleHog)在偵測時都會解碼 Base64 字串。如果你指望 Base64 能擋住它們,辦不到。

HTTP Basic 認證

// Authorization 標頭看起來像這樣:
Authorization: Basic dXNlcjpwYXNzd29yZA==

// 這段 Base64 一解就明:
atob("dXNlcjpwYXNzd29yZA==") // → "user:password"

HTTP Basic 認證只有在 HTTPS 上才安全 —— 真正的加密由 TLS 層提供。標頭中的 Base64 純粹是為了把 username:password 編碼為單一文字值,並不是為了隱藏。在明文 HTTP 上,任何攔截流量的人都能立刻讀到憑證。

把 JWT「token」當作秘密看待

// 一個 JWT 看起來是用點號連接的三段 Base64url:
eyJhbGciOiJIUzI1NiJ9.eyJ1c2VySWQiOjQyfQ.abc123

// 中間一段(payload)解碼後是:
atob("eyJ1c2VySWQiOjQyfQ") // → '{"userId":42}'

JWT payload 沒有被加密 —— 它們是 Base64url 編碼並被簽章的。任何拿到 token 的人都能讀 payload。簽章能防篡改,但不提供機密性。永遠不要把敏感資料(密碼、信用卡號、個人資訊)放進 JWT payload,除非你在用 JWE(JSON Web Encryption)—— 那是另一個、相當少見的標準。

在 URL 中混淆資料

// 舊應用中常見的模式
/profile?data=eyJ1c2VySWQiOjQyfQ==

// 貼到任何解碼器裡:
atob("eyJ1c2VySWQiOjQyfQ==") // → '{"userId":42}'

URL 中的 Base64 沒有安全增益。它還很容易出錯 —— 標準 Base64 中的 +/ 不是 URL 安全的,必須做百分號編碼,因此才有 Base64url 這個變體(+-/_,不填補)。

Base64 真正的用途

Base64 有合理且重要的用途 —— 只是不用來做安全:

  • 把二進位資料嵌入文字格式中 —— 把圖片、字型、檔案作為 data: URI 嵌入 HTML/CSS,或附在 JSON API 回應中
  • 電子郵件附件 —— MIME 編碼以 Base64 在僅支援 7 位元 ASCII 的協定上傳輸二進位檔案
  • 傳輸安全 —— 某些通道會破壞二進位資料(空位元組、控制字元);Base64 保證內容完整通過
  • 不透明識別符 —— 把 ID 或游標編碼為 Base64,向 API 使用者傳達「別試著解析它」,儘管它並不提供任何安全性

真正需要安全時該用什麼

如果你的目標是防止未授權存取資料,請使用真正的密碼學工具:

目標用什麼
對靜態資料加密AES-256-GCM(對稱加密)
對傳輸中資料加密TLS 1.3(HTTPS)
雜湊密碼bcrypt、scrypt 或 Argon2 —— 永遠不要只用 SHA-256
簽章 tokenHMAC-SHA256(如 JWT 的 HS256)或 RS256
存放秘密環境變數、秘密管理器(Vault、AWS Secrets Manager)
端對端加密 tokenJWE(JSON Web Encryption)—— 而不是普通 JWT

當你確實需要加密 token 時:JWE 與 JOSE

標準 JWT 是簽章的(JWS),不是加密的 —— payload 可讀。如果你真的需要讓 claims 具有機密性,請改用 JWE(JSON Web Encryption,RFC 7516)。JWE 屬於 JOSE 標準家族:

  • JOSE 演算法 —— 內容加密(例如 A256GCM)與金鑰管理(例如 RSA-OAEP-256ECDH-ESA256KW)。「alg」決定內容加密金鑰如何被包裝;「enc」決定 payload 本身如何被加密。
  • 金鑰包裝 —— JWE 為每條訊息產生新鮮的內容加密金鑰,並把它在接收方的金鑰下包裝(非對稱或對稱)。被包裝的金鑰隨 token header 傳送。這樣同一明文可以加密給不同接收方,而不必重新加密本體。
  • JWE 緊湊形式 —— 用點號連接的五段 Base64url(header.key.iv.ciphertext.tag),視覺上像 JWT,但是四段而非三段。

請選擇經過審計的函式庫(jose、node-jose、python-jose),不要自己實作 JWE —— 演算法與金鑰格式的組合很微妙,悄無聲息地回退到弱演算法是經典的 JOSE 漏洞。

針對你自己程式碼的快速自查

在你的程式碼庫中搜 btoa(atob(Buffer.from(…, 'base64')。對每一處問自己:

  • 我這樣做是為了讓文字能塞進某個欄位完整通過傳輸?→ 沒問題。
  • 我這樣做是為了對某人隱藏資料?→ 改用真正的加密或存取控制。

常見問題

Base64 是加密嗎?

不是。Base64 是一種沒有金鑰的可逆編碼 —— 任何人一次函式呼叫(atob())就能解碼。加密需要金鑰,沒有金鑰時還原原文是不可行的。

用 Base64 存密碼或 API 金鑰安全嗎?

不安全。解碼極其容易,金鑰掃描器還會自動解碼 Base64。請使用秘密管理器或環境變數,並以 bcrypt、scrypt 或 Argon2 雜湊密碼。

任何人都能讀 JWT payload 嗎?

能。JWT payload 是 Base64url 編碼並被簽章的,不是加密 —— 任何拿到 token 的人都能讀到每一個 claim。除非用 JWE,否則永遠不要把秘密放進 JWT。把 token 貼進線上工具有風險;見 為什麼不要把敏感 JSON 貼到線上工具中

Base64 究竟是用來做什麼的?

安全地把二進位資料表示為文字 —— data: URI、電子郵件(MIME)附件、以及在僅支援 ASCII 的通道上傳輸。它是包裝,不是安全措施。

在瀏覽器中編解碼

現在就想檢視一段 Base64 字串?fixjson.org 上的 Base64 編碼與解碼 工具能即時編碼或解碼任意字串 —— 完整 Unicode 與 UTF-8 都支援 —— 而且不需要安裝任何東西。在除錯 JWT payload、檢視 API 回應,或者看 data: URI 中藏了什麼時都有用。