Base64 是一种编码,不是加密。判断标准只有一条:加密需要密钥,编码不需要。Base64 的可逆性完全公开,任何人拿一个在线工具半秒就能还原原文,它提供的保密性是零。
Base64 要解决的问题是"二进制数据没法安全通过只接受文本的通道"。它把每 3 个字节(24 位)切成 4 组、每组 6 位,再把 6 位的值映射到一个 64 字符的可打印字符集(A-Z、a-z、0-9、+、/),不足 3 字节的部分用 = 补齐。
| 输入字节数 | 输出字符数 | 膨胀率 |
|---|---|---|
| 3 | 4 | 33.3% |
| 6 | 8 | 33.3% |
| 1 | 4 | 300% |
| 1 MB | 约 1.33 MB | 33.3% |
通项公式是 ceil(n / 3) * 4。也就是说,无论数据多大,编码后体积固定增加约三分之一。这是它在带宽敏感场景下的唯一硬成本,也是大文件不该内联进 JSON 或 HTML 的原因。
data:image/png;base64,...)、JSON 里传一段小图标、邮件 MIME 附件,都是典型用法。Authorization: Basic 后面跟的是 base64(username:password)。| 场景 | 错误做法 | 正确做法 |
|---|---|---|
| 存用户密码 | base64(password) |
Argon2id / bcrypt / scrypt |
| 传输 API 密钥 | 请求头里塞一层 Base64 就以为安全 | 走 HTTPS,服务端用 HMAC 签名 |
| 隐藏 URL 里的 ID | base64("user:10086") |
换成不可猜测的随机 ID(如 UUID) |
| 混淆配置里的数据库密码 | Base64 包一层 | 用密钥管理服务或环境变量 + 访问控制 |
| 保护用户隐私数据 | 编码后存库 | 用 AES-GCM 这类真加密,密钥独立管理 |
HTTP Basic 认证尤其容易被误解:它"看起来安全"是因为通常跑在 HTTPS 上,真正起作用的是 TLS,Base64 本身没有任何贡献。抓到明文 HTTP 流量,一眼就能解开。
URL 与文件名安全变体(RFC 4648 第 5 节)把 + 换成 -、/ 换成 _,通常还去掉 = 填充。JWT、OAuth 的 code、各种 URL 参数都用这一套。混用的后果很具体:一个带 + 的标准 Base64 串放进 URL 查询串,服务端会把它解成空格;带 / 的出现在路径里会被当成分隔符。
第一,btoa 处理不了中文。 浏览器的 btoa() 只接受 Latin-1(码位 0–255)字符,传中文会直接抛 InvalidCharacterError。正确做法是先做 UTF-8 编码:
function b64encode(str) {
const bytes = new TextEncoder().encode(str);
let bin = '';
for (const b of bytes) bin += String.fromCharCode(b);
return btoa(bin);
}
function b64decode(b64) {
const bin = atob(b64);
const bytes = Uint8Array.from(bin, (c) => c.charCodeAt(0));
return new TextDecoder().decode(bytes);
}
b64encode('你好'); // '5L2g5aW9'
b64decode('5L2g5aW9'); // '你好'
Node 里没有这个问题,直接指定编码即可:
Buffer.from('你好', 'utf8').toString('base64'); // '5L2g5aW9'
Buffer.from('5L2g5aW9', 'base64').toString('utf8'); // '你好'
第二,MIME 场景会自动插入换行。 邮件和一些老库每 76 个字符插一个 \r\n,直接拼接或比对会失败。处理前先去掉所有空白字符。
第三,Python 里别忘 urlsafe 变体。
import base64
base64.b64encode(b'\xfb\xff') # b'+/8='
base64.urlsafe_b64encode(b'\xfb\xff') # b'-_8='
| 编码 | 字符集大小 | 膨胀率 | 典型用途 |
|---|---|---|---|
| Hex(十六进制) | 16 | 100% | 哈希值展示、颜色值、调试输出 |
| Base32 | 32 | 60% | 需要大小写不敏感的场合(OTP 密钥、部分文件系统) |
| Base64 | 64 | 33% | 通用二进制转文本 |
| Base58 | 58 | 约 37% | 比特币地址一类,去掉了 0/O、l/I 等易混字符 |
选择依据只有三条:能不能容忍大小写、需不需要人眼抄写、对膨胀率有多敏感。只有前两条都不满足时才需要考虑 Base58 这类非标准方案,代价是要自己实现,标准库里没有。
三个条件一起看:
A-Z、a-z、0-9、+、/,末尾最多两个 =(URL 变体则是 - 和 _,通常不带填充)。满足前两条基本就能判定。但要记住:Base64 不携带任何类型信息,一段 Base64 解码后是文本还是 PNG,要靠字符集和文件头(‰PNG、PDF 的 %PDF-)进一步判断,不能靠猜。
只要需求里出现了"不能被别人看到"这几个字,Base64 就从选项里划掉。