qinghuan
← 回到知识探索

计算机趣闻 / 动手实验

网址里的百分号和 Base64,为什么藏不住文字?

把“能安全放进某种格式”与“只有持有密钥的人能读”分开,看一次编码和还原。

展开本文目录
  1. 百分号编码,先解决“标点到底属于谁”
  2. Base64 给二进制数据换一套文本写法
  3. 同一句话,来回走两条路
  4. 不要把“可传输”当成“不可窥看”
  5. 记住这三点
  6. 别把例子用过头
  7. 自己试一小步
  8. 查原始资料
  9. 找一份资料继续练
开始前,会这些就够了
  • 会运行一小段 Python,认识字符串、列表和条件判断即可
遇到不熟悉的词,先看这 2 个解释
字符编码
字符编码是把文字与可存储的数据对应起来的规则。写入时按规则产生数据,读取时按相应规则还原文字;双方使用不同规则,就可能看到乱码或替代符号。
加密
加密用算法和密钥把原来的内容变成难以直接阅读的数据,让拥有相应解密条件的人能够恢复内容。它主要解决谁能读到信息的问题,密钥的保存同样重要。
打开完整术语小抄 ↗

搜索一个带空格和中文的词,网址里可能多出一串百分号;接口里的一小块数据,又可能变成一行大小写字母。看起来陌生,不代表变成了秘密。这些写法常常只是为了让数据顺利通过某种文本格式。

这篇只看两种可逆编码。我们用一段虚构文字当材料,不讨论旧文章里的哈希指纹,也不使用真实密码。判断是否保密的关键,是还原时需不需要秘密,而不是肉眼能不能一眼看懂。

百分号编码,先解决“标点到底属于谁”

在 URL 的查询部分,& 和 = 可以承担分隔作用。如果用户搜索的文字本来含有这些符号,直接拼上去就可能被当成结构。百分号编码把相应字节写成 % 加两位十六进制数,让文字与分隔符各司其职。

不是看到一个 URL 就把它从头编码到尾。主机、路径和参数有各自的位置和规则;实际构造查询参数通常交给 URL 库。空格在一些表单编码里会写成 +,真正的加号则另行编码,所以也不能把所有 + 一律替换为空格。

Base64 给二进制数据换一套文本写法

Base64 把字节分组,映射成一套有限的可打印字符,便于放进只适合处理文本的地方。它没有用秘密密钥,知道规则的人就能解回字节。标准 Base64 和 URL 安全变体的字符约定有所不同,双方仍需说好使用哪一种。

换成这套写法通常会更长,而非压缩。普通 Base64 每三个输入字节对应四个字符,不足一组时还可能有填充。因此“内容更长、更像乱码”既不能证明加密,也不能证明压缩失败:编码本来就服务于另一个目的。

同一句话,来回走两条路

示例包含真正的加号、空格、& 和中文“茶”。quote 的 safe 参数设为空,表示把这段文字作为独立值处理,而不是把它当作一个完整 URL。Base64 则先按 UTF-8 得到字节,再编码;还原后也按 UTF-8 读文字。

两次还原都不需要提供密码。最后两行再对照普通百分号解码和表单式解码处理 a+b 的差异。它们不是谁比较聪明,而是遵守不同约定;生产代码应使用与发送方一致的解码方法。

小实验 · python
import base64
from urllib.parse import quote, unquote, unquote_plus

text = "A+B & 茶"
url_value = quote(text, safe="")
b64 = base64.b64encode(text.encode("utf-8")).decode("ascii")
print(url_value)
print(unquote(url_value) == text)
print(b64)
print(base64.b64decode(b64).decode("utf-8") == text)
print(unquote("a+b"))
print(unquote_plus("a+b"))

怎样运行:保存为 encoding_demo.py,在一个自己的练习目录中运行 python encoding_demo.py。使用 Python 3.9 或更高版本,仅用标准库。

运行后对照结果
A%2BB%20%26%20%E8%8C%B6
True
QStCICYg6Iy2
True
a+b
a b

不要把“可传输”当成“不可窥看”

给内部编号做 Base64,可能方便某个接口接收,但不会自动让编号难以猜测,更不会让服务端知道调用者有没有权限。服务端仍要按实际身份检查能否访问那条记录。把可逆的表示变化当作权限控制,会误解它能提供的作用。

同样,编码后的内容也没有因此变得可信。接收方还要按数据用途检查长度、类型和允许值。不要反复解码直到“看着正常”:一次解码后出现的百分号可能本来就是数据。每一层由谁编码、由谁解码,应该有明确约定。

记住这三点

  • 百分号编码帮助区分 URL 数据和结构;Base64 帮助以文本表示字节。
  • 两者都可按公开规则还原,没有提供保密性。
  • 字符编码、URL 所在位置和 Base64 变体都需要双方约定。

别把例子用过头

  • 不是所有 URL 位置都把 + 当空格。
  • Base64 不是压缩,也不会赋予内容访问权限或可信性。

自己试一小步

为一个“搜索词”参数设计可还原的小实验。

  1. 准备只含虚构内容的输入,覆盖中文、空格、加号和百分号。
  2. 分别用 quote 与表单查询构造方式处理,记录每条路使用的解码器。
  3. 加入一个已经含有 %20 的普通字符串,检查是否发生了多解码一次。

做到这些,就算完成

  • 每条路都能还原原始内容,而不只是外观相似。
  • 明确哪些标点是参数结构、哪些是参数值。
  • 解释为什么还原成功与保密无关,不用真实凭据做实验。

查原始资料

想确认一个细节,先看这份资料对应的部分,不必从头读完所有文档。

  • Percent-encoding (新标签页)↗MDN · 英文

    支持百分号表示、保留字符与空格在不同上下文中的表示差异。

    本次已打开并阅读相关正文;链接为原作者或官方资料。

  • RFC 4648:Base-N Encodings (新标签页)↗RFC Editor / Simon Josefsson · 英文

    支持 Base64 字母表、分组与填充;第 12 节说明这种编码不提供计算上的保密性。

    本次已打开并阅读相关正文;链接为原作者或官方资料。

  • urllib.parse:URL quoting (新标签页)↗Python Software Foundation · 英文

    支持 quote、unquote、unquote_plus 的调用约定与加号处理。

    本次已打开并阅读相关正文;链接为原作者或官方资料。

找一份资料继续练

先选一项,按下面的范围继续学;不必从头读完整门课程。