理解 Base64 編碼 — 完整開發者指南
Base64 編碼出現在網頁開發幾乎每個領域 — JWT 令牌、資料 URI、電子郵件附件、API 載荷、密碼學簽章與設定檔。儘管普遍存在,它經常被誤解:開發者有時將它與加密混淆,或在會讓情況更糟的情境中使用它。本指南準確解釋 Base64 如何運作、何時使用、何時避免,以及如何在 JavaScript 中正確使用。
Base64 編碼是什麼(以及不是什麼)
Base64 是將二進位轉為文字的編碼方式。它唯一的工作是將任意二進位資料轉換為可列印 ASCII 字元的字串。它不壓縮資料、不加密資料,也不驗證資料。它純粹是一種表示轉換 — 將可能包含空位元組、控制字元或會破壞文字協定的值的位元組,轉換為安全的字元集。
名稱來自編碼字母表中使用的 64 個可列印字元:A–Z(26 個字元)、a–z(26 個字元)、0–9(10 個字元)、加上 + 與 /(2 個字元)。= 字元用於填充。因為每個字元位置有 64 種可能值,且 2^6 = 64,每個 Base64 字元正好編碼原始資料的 6 位元。
Base64 如何運作 — 「Hello」逐步示範
理解 Base64 最好的方式是追蹤具體範例。讓我們編碼字串 Hello。
步驟 1:轉換為位元組
Hello 中的每個字元對應到其 ASCII 代碼,然後以二進位表示(每位元組 8 個位元):
H e l l o
72 65 6C 6C 6F (ASCII / 十六進位)
01001000 01100101 01101100 01101100 01101111 (二進位) 步驟 2:分組為 6 位元區塊
串接所有位元:0100100001100101011011000110110001101111(40 位元)。Base64 一次處理 3 位元組(24 位元)並將其映射為 4 個 Base64 字元(4 × 6 = 24 位元)。將 40 位元分組為 6 位元區塊,以零填充最後一組:
010010 000110 010101 101100 011011 000110 1111
18 6 21 44 27 6 (最後一組填充至 6 位元:111100 = 60) 步驟 3:映射到 Base64 字母表
每個 6 位元值映射到 Base64 字母表中的字元(A=0、B=1 ... Z=25、a=26 ... z=51、0=52 ... 9=61、+=62、/=63)。由於 5 位元組不是 3 的倍數,新增一個 = 填充字元:
索引: 18 6 21 44 27 6 60
字元: S G V s b G 8
結果: SGVsbG8= (= 是填充) 結果
"Hello" → "SGVsbG8="
你可以使用 Toova Base64 編碼/解碼器立即驗證 — 貼上 Hello、點擊編碼,你會得到 SGVsbG8=。點擊解碼即可反向轉換。
33% 的大小額外開銷
Base64 編碼永遠將資料大小增加約 33%。數學很直接:3 位元組的輸入(24 位元)產生 4 個 Base64 字元(24 位元編碼於 4 × 6 位元組)。這是 4/3 的比例,即 33.3% 的額外開銷。加上填充字元,實際額外開銷介於 33% 至 36% 之間,取決於輸入長度。
這對效能有顯著影響。在 HTML 中作為 Base64 資料 URI 嵌入的 1 MB 圖片約成為 1.37 MB。對所有二進位載荷以 Base64 編碼的 API 比必要傳送多 33% 的資料。對於短令牌或 checksum 等小值,額外開銷可忽略。對於大型檔案,則是實質成本。
URL 安全變體
標準 Base64 使用 + 與 / 作為最後兩個字母表字元。這兩個在 URL 中都會造成問題:
+在查詢字串中被解碼為空格字元/是 URL 中的路徑分隔符
URL 安全 Base64(也稱為 Base64url,定義於 RFC 4648 第 5 節)將 + 換為 -,並將 / 換為 _。在 URL 安全情境中通常省略填充(=),因為它可能被某些 URL 解析器誤解。
JWT 令牌使用無填充的 Base64url。當你手動解碼 JWT 標頭或載荷時,必須同時處理字元替換與遺漏的填充。以下是在 JavaScript 中的做法:
// URL 安全 Base64(將 + 換為 -,將 / 換為 _)
function toBase64Url(base64) {
return base64.replace(/\+/g, '-').replace(/\//g, '_').replace(/=/g, '');
}
function fromBase64Url(base64url) {
const padded = base64url.padEnd(
base64url.length + (4 - base64url.length % 4) % 4,
'='
);
return atob(padded.replace(/-/g, '+').replace(/_/g, '/'));
} Toova Base64 編碼/解碼器透過單一開關同時支援標準與 URL 安全變體。
JavaScript 中的 Base64
瀏覽器:btoa() 與 atob()
瀏覽器提供兩個內建函數:btoa()(binary to ASCII,即編碼)與 atob()(ASCII to binary,即解碼)。儘管名稱順序令人混淆,它們在瀏覽器中已可用超過十年。
// 瀏覽器 — atob / btoa(僅字串,ASCII 安全)
const encoded = btoa("Hello");
console.log(encoded); // "SGVsbG8="
const decoded = atob("SGVsbG8=");
console.log(decoded); // "Hello"
重要限制:btoa() 只接受含 Latin-1 範圍內字元(代碼點 0–255)的字串。如果你傳入含 Unicode 字元(例如 emoji 或 CJK 字元)的字串,會擲出 DOMException。要編碼任意二進位資料,請先將其轉換為 Uint8Array:
// 瀏覽器 — 編碼任意二進位(Uint8Array)
const bytes = new Uint8Array([72, 101, 108, 108, 111]);
const encoded = btoa(String.fromCharCode(...bytes));
console.log(encoded); // "SGVsbG8="
對於可能包含 Unicode 字元的任意字串編碼,建議的現代做法是先使用 TextEncoder 取得 Uint8Array,然後如上所示進行編碼。
Node.js:Buffer
Node.js 提供 Buffer 類別,能正確處理二進位資料,並支援多種編碼,包括 Base64 與 Base64url:
// Node.js — Buffer(安全處理二進位)
const encoded = Buffer.from('Hello').toString('base64');
console.log(encoded); // "SGVsbG8="
const decoded = Buffer.from('SGVsbG8=', 'base64').toString('utf8');
console.log(decoded); // "Hello"
// Node.js 中的 URL 安全變體
const urlSafe = Buffer.from('Hello').toString('base64url');
console.log(urlSafe); // "SGVsbG8"(無填充) base64url 編碼選項(自 Node.js 16 起可用)會自動處理字元替換與填充移除,使其比手動轉換容易得多。
對需要處理大型二進位檔案的瀏覽器環境,FileReader.readAsDataURL 方法會將檔案編碼為 Base64 資料 URI,而不必一次將所有內容載入記憶體。
何時使用 Base64
在純文字協定中嵌入二進位資料
Base64 的原始用例是在 SMTP 中編碼二進位電子郵件附件,SMTP 是僅支援 7 位元 ASCII 文字的協定。相同原則適用於任何需要將二進位資料包含於無法處理原始位元組之格式中的情境:JSON API 載荷、XML 文件、HTML 屬性、CSS 值、HTTP 標頭。
小型資產的資料 URI
CSS 與 HTML 允許你將圖片、字型與 SVG 作為 Base64 資料 URI 嵌入。這消除小型資產(例如圖示)的 HTTP 往返,並消除對首屏關鍵圖片的「未經樣式內容閃爍」。
<!-- 行內 SVG 圖示作為 Base64 資料 URI -->
<img src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0c..." alt="icon" />
<!-- 行內 CSS 背景 -->
.icon {
background-image: url("data:image/png;base64,iVBORw0KGgo...");
} 取捨:Base64 資料 URI 無法獨立於包含它們的 HTML/CSS 檔案進行快取。如果圖片從不變更但周圍 HTML 變更,瀏覽器在每次頁面重新載入時都會重新下載圖片資料。資料 URI 僅用於小型資產(理想上 4 KB 以下),且消除往返的好處超過快取代價時。
為 JSON 或 URL 參數編碼二進位資料
JSON 是文字格式 — 它無法直接表示原始二進位位元組。當 API 需要傳輸二進位資料(圖片縮圖、密碼學簽章、壓縮資料)時,Base64 是將其包含於 JSON 載荷中的標準方式。同樣地,如果你需要在 URL 查詢參數中傳遞二進位資料,Base64url 編碼確保資料在百分號編碼中不會毀損。
JWT 與其他令牌格式
JWT 令牌使用 Base64url 編碼其標頭與載荷區段。這讓令牌成為可列印、URL 安全的字串,可在 HTTP 標頭、cookies 或 URL 參數中傳遞。編碼並非為了安全(任何擁有令牌的人都能讀取載荷)— 它純粹為了安全傳輸。
何時「不」使用 Base64
安全或機密性
Base64 不提供任何安全。它在毫秒內就能反向轉換。不要用它「混淆」密碼、API 金鑰或敏感設定值。任何看到 Base64 字串的開發者會立即解碼它。如果你需要機密性,請使用加密。
密碼儲存
儲存 Base64 編碼的密碼等同於以明文儲存 — 編碼可即時反向。密碼必須使用適當的密碼雜湊函數,例如 bcrypt、Argon2 或 scrypt 進行雜湊。
大型二進位檔案
將 10 MB 檔案編碼為 Base64 會產出 13.7 MB 的字串。如果你將其儲存於資料庫欄位、進行搜尋,或透過 API 傳輸,每次都會付出 33% 的額外開銷。對於大型二進位資料,請使用專用二進位儲存:資料庫 BLOB/BYTEA 欄位、像 S3 或 GCS 的物件儲存,或直接串流二進位。
可直接使用二進位的情境
如果你的協定或格式支援原始二進位 — 例如,具二進位訊息類型的 WebSocket、multipart/form-data HTTP 上傳,或二進位檔案格式 — 直接使用二進位。Base64 只在傳輸媒介確實無法處理原始位元組時才必要。
常見陷阱
混淆編碼與加密
這是最常見的錯誤。Base64 是可見的。它不是資安機制。像「密碼為了安全以 Base64 編碼儲存」這樣的程式碼註解表示嚴重的誤解,應在程式碼審查中被抓到。
對 Unicode 字串使用 btoa()
對含代碼點高於 255 字元的字串呼叫 btoa(),會擲出 DOMException: Failed to execute 'btoa': The string to be encoded contains characters outside of the Latin1 range。對可能含 Unicode 字元的字串編碼前,務必透過 TextEncoder 轉換為 Uint8Array。
解碼時忘記填充
Base64 字串的長度必須是 4 的倍數。如果 Base64 字串無填充產生(在 URL 安全編碼中常見),你必須在解碼前加回正確數量的 = 字元。長度為 n 的 Base64 字串需要 (4 - n % 4) % 4 個填充字元。忘記這點會造成難以診斷的解碼錯誤。
雙重編碼
Base64 字串本身是有效 ASCII,因此 btoa(btoa(data)) 不會出錯但會產出雙重編碼輸出。當在多層序列化中傳遞 Base64 值時(例如 JSON 中的 JSON),容易將相同資料編碼兩次。永遠以你編碼的相同次數解碼。
快速參考:實務中的 Base64
若需在瀏覽器中編碼與解碼而不撰寫程式碼,Toova Base64 編碼/解碼器完全在你的瀏覽器中執行 — 無伺服器往返。它支援標準與 URL 安全變體、上傳檔案以編碼二進位檔案,並為解碼後的資料同時提供文字與十六進位輸出。
如果你處理 URL 內的編碼內容,URL 編碼/解碼器會分別處理百分號編碼與 Base64。對於 HTML 實體,HTML 實體轉換器會處理 HTML 情境中的字元跳脫。這些是不同的編碼方式 — 各有特定用例。
Base64 的權威參考是 RFC 4648,其中定義標準 Base64(第 4 節)、Base64url(第 5 節)與 Base32(第 6–7 節)。對於 btoa() 與 atob() 瀏覽器 API,btoa() 的 MDN 文件詳細涵蓋瀏覽器相容性與 Unicode 限制。
總結
Base64 編碼使用 64 字元字母表將二進位資料轉換為可列印 ASCII。它將資料大小增加 33%、完全可反向,並不提供安全。在你需要將二進位資料嵌入文字格式時使用它 — JSON 載荷、HTML 資料 URI、JWT 令牌、電子郵件附件、URL 參數。當你需要安全、傳輸直接支援二進位,或 33% 額外開銷在規模上很重要時,避免使用它。
理解 Base64「是什麼」以及「不是什麼」,能避免最常見的錯誤:用於安全、對大型檔案不必要地套用,以及與 URL 編碼或 HTML 實體等其他編碼方式混淆。每種編碼方式解決特定問題。Base64 確切解決一個:讓二進位資料能在純文字頻道中安全傳輸。
準備好編碼或解碼了嗎?試試 Toova Base64 編碼/解碼器 — 貼上文字或拖放檔案、切換標準或 URL 安全,並複製結果。無需帳戶、無伺服器、無使用限制。