「我把它 Base64 编码一下,这样就没人能读了。」这句话出现在代码评审、Slack 消息以及 Stack Overflow 答案里的频率,比它应该的高得多。Base64 不是加密。它不是混淆。它不是任何形式的安全机制。把它解码回来只需要一次函数调用,且不需要任何密钥或秘密。本文讲清楚 Base64 到底是什么、误解为何根深蒂固,以及当你真的需要保护数据时该用什么。
Base64 实际是什么
Base64 是一种编码方案 —— 二进制数据与 64 个可打印 ASCII 字符(A–Z、a–z、0–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 |
| 签名 token | HMAC-SHA256(如 JWT 的 HS256)或 RS256 |
| 存放秘密 | 环境变量、秘密管理器(Vault、AWS Secrets Manager) |
| 端到端加密 token | JWE(JSON Web Encryption)—— 而不是普通 JWT |
当你确实需要加密 token 时:JWE 与 JOSE
标准 JWT 是签名的(JWS),不是加密的 —— payload 可读。如果你真的需要让 claims 具有机密性,请改用 JWE(JSON Web Encryption,RFC 7516)。JWE 属于 JOSE 标准家族:
- JOSE 算法 —— 内容加密(例如
A256GCM)与密钥管理(例如RSA-OAEP-256、ECDH-ES、A256KW)。「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 里藏了什么时都有用。
- Base64 编码与解码 —— 即时、在浏览器中运行、数据不上传任何服务器
- RFC 4648:Base64 标准 —— Base64 与 Base64url 的历史与正式定义
- 如何解码 JWT —— 读 token 的 claims,以及解码为什么不等于验证
- RFC 7519:JSON Web Token(JWT) —— Base64url 如何在 JWT 标准中使用,以及围绕 JWT payload 的安全考量
- URL 解码 —— 对 URL 或查询字符串做百分号解码
- JSON Stringify —— 把 JSON 值编码为转义后的字符串字面值