Base64详解:6位分组、约33%大小膨胀、填充与base64url对比标准
Base64是什么——以及它不是什么
Base64是一种二进制转文本的编码方案。它将任意二进制数据转换为可打印的ASCII字符串,使该数据可以安全地嵌入到为人类可读文本设计的环境中。Base64不是压缩——它会让数据变得更大。它也不是加密——任何拥有编码字符串的人都能轻易还原原始字节。其名称来自它使用恰好64个字符的字母表,因为2⁶=64,每个字符恰好可以代表6位的输入。
Base64并非作为独立标准而发明;它源于需要通过只能可靠传输7位ASCII的SMTP基础设施发送二进制电子邮件附件的需求。RFC 2045(1996年)定义了MIME,并为电子邮件标准化了这种编码。十年后,RFC 4648(2006年)将base64及其变体——包括base32和base64url——整合到单一参考文档中。RFC 4648至今仍是任何需要二进制转文本编码的新协议所使用的权威规范。
6位分组:三个输入字节如何变成四个字符
Base64的核心是一个简单的位重新打包操作。二进制数据以8位字节的形式到达;base64将这些位重新分组为6位的块,每个块映射到64个字符字母表中的一个字符。因为6和8的最小公倍数是24,自然的处理单位是3字节=24位→4个字符(4×6位)。以ASCII字符串Man为具体例子:三个字节为0x4D、0x61、0x6E,得到位序列01001101 01100001 01101110。重新分组为四个6位值:010011(19→T)、010110(22→W)、000101(5→F)、101110(46→u)。编码结果为TWFu。
这个3对4的字节比率是大小膨胀的直接来源。对于每3个输入字节,无论实际字节值为何,输出始终是4个字符。对于n个字节的输入,编码输出为ceil(n/3)×4个字符(在填充之前),这意味着输出始终比输入大33%(精确地说是4/3倍)。1KB的文件编码后约为1.37KB;1MB的图像编码后约为1.37MB。
字母表:A–Z、a–z、0–9、+、/
标准base64字母表按以下顺序将6位值0–63映射到可打印的ASCII字符:值0–25映射到大写A–Z,值26–51映射到小写a–z,值52–61映射到数字0–9,值62映射到+,值63映射到/。这种顺序将字母置于数字之前——与ASCII数字顺序相反——出于根植于MIME兼容性的历史原因。+和/在URL中具有特殊含义,几十年来造成了互操作性问题。
一个常见的误解是base64与字符集无关。事实并非如此:字母表是固定的ASCII。遇到65个字符集(64个数据字符加上用于填充的=)之外的字符的解码器,必须根据规范上下文拒绝输入或跳过它。RFC 4648建议在严格模式下拒绝非字母表字符。等号=是一个标记,不是数据字符——它标记填充,不应出现在编码字符串末尾以外的位置。
填充:等号的存在原因以及何时可以省略
由于编码每次处理3个字节,长度不能被3整除的输入在末尾会留下一个不完整的组。填充解决了这个问题:它强制使编码输出始终是4个字符的倍数,使得可以仅从输出长度计算原始字节数。如果输入有1个余数字节,则附加2个填充字符==。如果输入有2个余数字节,则附加1个填充字符=。
在通过其他方式已知总长度的上下文中,填充是可选的。RFC 7515(JSON Web签名规范)明确要求实现在编码JWT组件时省略=填充,因为JWT三个部分之间的点已经界定了各段。因此,JWT中的base64url编码值从不以=结尾。恢复填充很简单:在字符串长度是4的倍数之前附加=字符。大多数现代base64库无需特殊配置即可接受带填充和不带填充的输入。
Base64url:JWT和OAuth令牌中使用的URL安全变体
标准base64的+和/字符在URL上下文中存在问题。在查询字符串中,+被解释为空格;/是路径段分隔符。Base64url定义于RFC 4648第5节,将+替换为-,将/替换为_。这两个字符在URL中无需百分比编码即可安全使用。在base64url上下文中通常也省略填充=,以避免URL中出现%3D。
Base64url用于需要紧凑、URL安全的二进制表示的任何地方:JWT的标头和有效载荷部分是base64url编码的JSON;OAuth 2.0授权码通常是base64url编码的随机字节;PKCE(RFC 7636)代码验证器和挑战也使用base64url。数据膨胀比率与标准base64完全相同——恰好4/3——因为只有字母表不同,位分组算法相同。解码base64url与解码标准base64的操作相同,只需先将-→+,_→/进行替换。
实际大小影响以及如何在base64和二进制之间选择
约33%的开销是base64的主要实际成本。对于Data URI——使用data:image/png;base64,...直接在HTML或CSS中嵌入图像、字体或SVG——这种权衡是消除一次HTTP往返,代价是更大的文档。这对于小型资源(通常低于4–8KB)有益:节省的往返时间超过了大小增加,尤其是在高延迟连接上。对于较大的资源,大小增加会降低性能,因为更大的HTML文档需要更长时间解析,阻塞渲染,且无法与页面分开缓存。
HTTP基本验证将凭证编码为Authorization: Basic ...标头中的base64(用户名:密码)。这不是安全措施——用户名和密码可以通过解码轻易还原。基本验证需要HTTPS才能安全;base64编码的存在仅因为HTTP标头规范要求可打印的ASCII。如果有效载荷包含敏感信息——私钥、社会安全号码、医疗记录——base64编码不能替代加密。请使用已验证加密(例如AES-GCM)或JSON Web加密(JWE,RFC 7516)来保护机密数据。