Python Base64 严格解码:字符合法之后,还要约定字母表与填充
上传接口收到一段编码字符串,解码成功并不一定说明输入格式符合协议。Python 的 Base64 默认解码会先丢弃不属于字母表的字符,再检查填充。因此,把异常捕获当作唯一的输入验收,有时会放过原本应该拒绝的标点与空白。
先为接口写一个小约定:本文只接受带必要填充的 URL-safe 编码,不接受换行,不接受普通字母表中的加号与斜杠,并要求同一组字节只有一种文本表示。这个约定是示例的业务选择;接收邮件正文或已有协议时,应遵守各自规则。
AI概念配图,非真实界面:以抽象物件说明本文主题,不代表运行结果。
把三个检查放在同一个入口
下面程序保存为 demo.py 后用 Python 3 运行,不需要联网或第三方库。开头先演示宽松行为,再定义带长度上限的入口。上限针对编码字符串的字符数,目的是在分配解码结果之前拒绝明显过大的请求;业务仍应在读取请求时限制输入体积。
严格参数负责拒绝杂字符,但传入替代字母表不等于禁止标准字母表。代码因此先明确拒绝加号和斜杠,再解码,最后重新编码比较原文。这个往返比较还能发现末尾未使用位不为零等非规范形式,避免多个字符串映射到相同字节。
import base64
import binascii
assert base64.b64decode(b"aG!k=") == b"hi"
try:
base64.b64decode(b"aG!k=", validate=True)
except binascii.Error:
pass
else:
raise AssertionError("noise accepted")
def decode_urlsafe(text, max_chars=4096):
if not isinstance(text, str):
raise TypeError("expected text")
if len(text) > max_chars:
raise ValueError("too long")
if "+" in text or "/" in text:
raise ValueError("standard alphabet forbidden")
try:
raw = base64.b64decode(text, altchars=b"-_", validate=True)
except (ValueError, binascii.Error) as exc:
raise ValueError("invalid base64") from exc
if base64.urlsafe_b64encode(raw).decode("ascii") != text:
raise ValueError("non-canonical representation")
return raw
raw = b"\xfb\xff"
standard = base64.b64encode(raw).decode("ascii")
safe = base64.urlsafe_b64encode(raw).decode("ascii")
assert standard == "+/8=" and safe == "-_8="
assert decode_urlsafe(safe) == raw
assert decode_urlsafe("") == b""
for bad in ["aG!k=", "aGk", "aGk=\n", "+/8=", "Zh==", "中"]:
try:
decode_urlsafe(bad)
except ValueError:
pass
else:
raise AssertionError(bad)
print(standard, safe)
print("strict alphabet, padding and canonical checks passed")解码层和协议层各自负责什么
程序输出两种字母表以及验收通过提示。第一条断言故意加入感叹号,证明默认行为可以忽略杂字符;严格调用则必须抛出异常。后续样本覆盖缺失填充、末尾换行、标准字母表、非规范填充位和非英文字符,任何一个意外通过都会让程序失败。
最后的空字符串会还原为空字节,这在编码层面有效。如果字段代表必须存在的附件或标识,应在业务层另加非空要求。不要把“编码合法”扩展成“内容有意义”,也不要先把所有空格删掉再声称自己做了严格验证。
带填充和无填充是两个不同的接口约定。若对端明确要求省略等号,可以在另一份清晰的实现里检查长度和允许字符,再补回所需填充。不要在一个未知格式的入口里不断尝试不同变体,直到某个解码器终于给出结果。
接回业务之前再检查原始内容
还原后的结果是字节,不保证是文本。只有业务声明使用某种字符编码时才继续解码成字符串;二进制图片或压缩数据应交给相应格式解析器。把字符编码异常与 Base64 格式异常分开记录,会让排查更接近真正出错的层次。
Base64 只改变表示方式,不提供保密性,也不证明数据来自可信来源。示例的规范化比较用于统一文本形态,不能替代签名、消息认证或权限校验。若原文参与签名,必须按协议规定处理原始字节,不能擅自改写后再验证。
上线验收时,把可接受形式、最大长度、空值政策和错误分类写进接口说明。日志通常只需记录长度与失败类别,不必完整打印用户提交的内容。这样,前后端能围绕同一组正反样本协作,而不是依赖某个解码函数恰好有多宽松。


