什么是Base64?
Base64是一种将二进制数据表示为可打印ASCII字符序列的编码方案。它可以将任意字节序列——无论是图片、PDF文件还是原始二进制数据——编码为仅由64个安全字符组成的字符串:A–Z、a–z、0–9、+和/(用=进行填充)。
"Base64"这个名称源于该编码使用了以64为基数的数字系统。
64个字符
| 分组 | 字符 |
|---|---|
| 大写字母 | A–Z(26个) |
| 小写字母 | a–z(26个) |
| 数字 | 0–9(10个) |
| 符号 | +、/(2个) |
| 填充符 | = |
此外还有URL安全变体,将+替换为-,将/替换为_,以避免与URL特殊字符冲突。
Base64编码的工作原理
Base64通过将每3个字节的二进制数据转换为4个Base64字符来实现编码。
- 取输入的3个字节(24位)
- 分割为四个6位组
- 将每个6位值映射到Base64字母表中的一个字符
示例:对字符串"Man"进行编码
| 字符 | M | a | n |
|---|---|---|---|
| ASCII码 | 77 | 97 | 110 |
| 二进制 | 01001101 | 01100001 | 01101110 |
合并:010011010110000101101110
分割为6位组:010011 | 010110 | 000101 | 101110
映射:T | W | F | u → "TWFu"
如果输入不是3字节的倍数,则使用=字符进行填充以补全最后一组。
数据大小开销
Base64编码会使数据大小增加约33%(每3个字节变为4个字符)。在决定是否对大型数据使用Base64时需要考虑这一因素。
发展历史
Base64是在MIME(多用途互联网邮件扩展)的背景下发展起来的,于1992年通过RFC 1341标准化。电子邮件系统最初只能传输7位ASCII文本,但图片和文档等附件都是二进制数据。Base64通过将二进制数据编码为纯文本来解决这一问题,使其能够安全地通过电子邮件基础设施传输。
这个名称和概念甚至在Unix的uuencode(一种不同但相关的方案)中更早出现,但MIME的Base64成为了持久的标准。
常见使用场景
1. 在HTML/CSS中嵌入图片
除了引用外部图片文件,你还可以使用Base64编码的数据URI直接嵌入图片:
<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAA..." />
适用场景:在关键渲染路径中可以减少HTTP请求的小图标或图片。
不适用场景:大型图片——33%的大小开销加上无法单独缓存图片,使其弊大于利。
2. HTTP基本认证
HTTP基本认证将凭据作为Base64编码字符串发送在Authorization请求头中:
Authorization: Basic dXNlcm5hbWU6cGFzc3dvcmQ=
这是用Base64编码的username:password。重要提示:这不是加密——它可以被轻易解码。使用基本认证时务必配合HTTPS。
3. JWT(JSON Web Token)
JWT令牌由三个用点分隔的Base64URL编码部分组成:头部、载荷和签名。头部和载荷是可读的(未加密)——Base64在这里纯粹用于传输编码。
4. 电子邮件附件(MIME)
作为Base64的原始使用场景:电子邮件客户端将二进制附件(图片、PDF等)编码为Base64,以便通过电子邮件基础设施安全传输。
5. 在JSON或XML中存储二进制数据
由于JSON和XML只支持文本,二进制数据(如加密密钥或小图片)通常在包含之前进行Base64编码。
6. CSS中的数据URL
背景图片可以内联到CSS中:
background-image: url('data:image/svg+xml;base64,PHN2ZyB4...');
Base64变体
| 变体 | 字符 | 使用场景 |
|---|---|---|
| 标准Base64 | A–Z a–z 0–9 + / | 通用目的 |
| URL安全Base64 | A–Z a–z 0–9 - _ | URL、文件名、JWT |
| 无填充Base64 | 省略= | 某些API首选此格式 |
| MIME Base64 | 标准格式+每76字符换行 | 电子邮件 |
安全注意事项
Base64不是加密
这是最关键的一点:Base64是编码,不是加密。任何拥有Base64字符串的人都可以立即解码它——不需要密钥。永远不要使用Base64来"隐藏"敏感信息。
echo "dXNlcm5hbWU6cGFzc3dvcmQ=" | base64 --decode
# 输出:username:password
不要用Base64编码密码
将密码存储为Base64提供的安全性为零。应该使用适当的密码哈希算法,如bcrypt、Argon2或scrypt。
数据URI与XSS风险
虽然Base64数据URI本身是安全的,但在某些浏览器上下文中,恶意JavaScript可以通过data:text/html;base64,...这样的URI进行编码和执行。大多数现代浏览器对链接导航和iframe来源限制了这种方式,但始终要对用户提供的数据URI进行净化处理。
各编程语言中的Base64
JavaScript
// 编码
const encoded = btoa("Hello, World!"); // "SGVsbG8sIFdvcmxkIQ=="
// 解码
const decoded = atob("SGVsbG8sIFdvcmxkIQ=="); // "Hello, World!"
注意:btoa/atob在浏览器中只适用于Latin-1字符串。对于Unicode:
// Unicode安全编码
const encoded = btoa(unescape(encodeURIComponent("こんにちは")));
或在Node.js中使用Buffer:
Buffer.from("Hello").toString("base64");
Buffer.from("SGVsbG8=", "base64").toString("utf8");
Python
import base64
# 编码
encoded = base64.b64encode(b"Hello, World!").decode("utf-8")
# "SGVsbG8sIFdvcmxkIQ=="
# 解码
decoded = base64.b64decode("SGVsbG8sIFdvcmxkIQ==").decode("utf-8")
# "Hello, World!"
# URL安全变体
url_safe = base64.urlsafe_b64encode(b"Hello+World/")
性能考量
- 编码/解码速度快:在大多数使用场景中,Base64的开销可以忽略不计。
- 大小开销:比二进制大约33%。避免对频繁传输的大文件(MB级别以上)使用。
- CPU缓存压力:在内存中对大型二进制文件进行编码可能会给CPU缓存带来压力。
对于大型二进制传输,优先选择二进制协议(带二进制帧的HTTP/2、WebSocket二进制帧、gRPC配合protobuf),而非Base64。
总结
Base64是那些构成现代Web基础的技术之一,大多数开发者在不知不觉中就使用了它。它很好地解决了一个特定问题:在基于文本的系统中安全地将二进制数据编码为可打印文本进行传输。
使用Base64的场景:需要在文本上下文中嵌入二进制数据(HTML、JSON、电子邮件、请求头)。
不应使用Base64的场景:想要加密或保护数据——Base64完全可以在没有任何密钥的情况下被逆向解码。