V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
drymonfidelia
V2EX  ›  程序员

为什么最流行的编码算法是刚好带两个符号破坏兼容性的 base64,而不是能够无视大小写的 base36、不带符号的 base62?这两个符号不仅严重影响兼容性使标准码表的 base64 不能直接拼在 url 中,还没有增加多少信息密度

  •  1
     
  •   drymonfidelia · 83 天前 · 5635 次点击
    这是一个创建于 83 天前的主题,其中的信息可能已经有所发展或是发生改变。

    base64 的编码结果并不是二进制,码表长度是 2 的整数次方没有任何的加成。

    被 base64 坑了好几次,按=截断 key=value 数据的时候忘记指定 maxsplit=1 导致=后面的数据丢掉。

    49 条回复    2024-09-02 11:50:16 +08:00
    MoYi123
        1
    MoYi123  
       83 天前
    不是有 base64url 吗?
    drymonfidelia
        2
    drymonfidelia  
    OP
       83 天前
    @MoYi123 我说了 **标准码表** 啊,我是想讨论兼容性这么差的编码算法为什么会流行起来
    MoYi123
        3
    MoYi123  
       83 天前   ❤️ 1
    @drymonfidelia 因为 n % 64 == n & (63), 而 base62 和 base36 不行, 就这么简单.
    vvhy
        4
    vvhy  
       83 天前   ❤️ 3
    字符串分割可不是处理 url 的好方法
    Trim21
        5
    Trim21  
       83 天前
    码表长度是 2 的整数次方当然有加成啊,这意味着不需要进行大整数运算。
    masterclock
        6
    masterclock  
       83 天前   ❤️ 3
    base64 发明的时候 http 还没被发明出来
    bczhc
        7
    bczhc  
       83 天前
    因为 base64 广泛啊,multipart 里就能用它。而且 64 是 2 整数次方能方便编解码,重新对二进制位分隔就好了。而且其他的 binary-to-text 编码也有,base58 ,base62 base85 base91 等。你说 base32 ,咋不直接用 base16 ,就直接是十六进制咯。最后,base64url 也是事实上的规范。
    drymonfidelia
        8
    drymonfidelia  
    OP
       83 天前
    @Trim21 多用几位一起编码不会带来明显的性能损失吧,和兼容性比起来两个符号的问题显得更大
    tool2dx
        9
    tool2dx  
       83 天前
    base62 解码写起来很麻烦的,代码量要比 base64 多。你写过一次,就知道为什么没有流行起来了。

    总最简原则来讲,base64 还是方便不少。
    sagaxu
        10
    sagaxu  
       83 天前
    base64: 3 字节的东西编码进 4 字节
    hex(base16): 2 字节的东西编码进 4 字节
    bit 数是固定的,编码解码位运算很方便

    base36 ?你告诉我是几字节到几字节?
    占几个 bit 固定不固定?如果不固定,可变长时怎么标记位数?如果固定,那 base 一定是 2 的某个次方,比如 16/32/64/128
    ipwx
        11
    ipwx  
       83 天前
    @drymonfidelia 本来位运算就比除法快一个数量级。

    更何况为了速度,base64 都上 SSE/AVX2/AVX512 向量指令集了。

    在编码方面,速度就是这么重要。
    ipwx
        12
    ipwx  
       83 天前   ❤️ 1
    另外看楼主的背景是前端或者后端。

    如果楼主会写 C ,大概才能理解为啥 64 位一定比 62 位快很多了。
    Trim21
        13
    Trim21  
       83 天前
    @drymonfidelia #8 你首先要理解 baseXX 的意义,是把原始数据作为一个大整数然后进行进制转换。

    base62 ,base63 跟 base65 可能差不多,但是 base64 有独特的性质。64=2^6 ,byte 的 255=2^8 意味着每 3 字节的原始数据一定能编码为 4 字节的 base64 。

    所以 base64 可以每 4byte 进行处理,用现成的 uint32 就行。换成 base62 你就要处理一个不定长的 big int ,因为 62 不是 2 的幂,没有 base64 这样的性质。
    proxytoworld
        14
    proxytoworld  
       83 天前   ❤️ 2
    挺奇怪的,又不是没有 url 编码,啥东西 url 编码之后拼在 url 也不会有问题啊,而且 base64 一开始设计也不是给 http 用的,只是刚好能用在 http 上
    proxytoworld
        15
    proxytoworld  
       83 天前
    没看懂影响兼容性,url 编码解码不费 cpu ,又不是 api 改参数了。
    IvanLi127
        17
    IvanLi127  
       83 天前   ❤️ 2
    这样说的话,主流 base64 不适合你这个用例,没必要上,谁逼你上的直接干他。坑你的人是主张在 url 里放乱七八糟东西的人,而不是 base64 。
    Y25tIGxpdmlk
        18
    Y25tIGxpdmlk  
       83 天前
    base64 我记得好像是 3 位一组 3 位一组,然后不够的用=号补齐。用 62 的话,相似的设计,需要单独一个字符专门用来当补齐符号用。
    wy315700
        19
    wy315700  
       83 天前
    因为 3 * 8 = 4 * 6

    而 2 ^ 6 = 64 所以挑了 64 个字符
    knva
        20
    knva  
       83 天前
    挺怪的.
    liuidetmks
        21
    liuidetmks  
       83 天前
    避免除法,
    你有 1G 数据流,直接当做一个整数,那是很麻烦的
    而且可以流式处理,不用等到所有数据都接受到了才编码
    CEBBCAT
        22
    CEBBCAT  
       83 天前   ❤️ 1
    菜就多练,URL 传递 Base64 就用 Base64URL ,硬传不就等于自己给自己挖坑吗?用的哪个语言?没有便捷的库?
    lisongeee
        23
    lisongeee  
       83 天前   ❤️ 4
    > 被 base64 坑了好几次,按=截断 key=value 数据

    看起来你的场景是在反序列化 url string 里的 search 参数,好奇为什么不用标准序列化对象?

    https://developer.mozilla.org/en-US/docs/Web/API/URL

    另外将 base64 编码到 url search 参数里的时候,也要调用标准序列化方法

    此时 base64 里的 = 字符会变成 %3D ,如果按照这个标准序列化,你的分割不会出现错误

    我猜测两边都是手动拼接/手动分割字符串去构造参数,而不是去使用标准序列化和反序列化方法

    我们这边后端一个系统 解析/构造 url 的时候不按照标准走,产生如 hash 丢失,参数解码错误破坏整个 url

    还有 飞书 的网页第三方登录,点击拒绝授权的时候,如果你的参数里面有 url ,url 里面有特殊字符,虽然你的按标准走的,但是煞笔飞书会手动解码两次后拼接,导致破坏整个 url 导致参数丢失

    每次跟这些不按标准喜欢自己拼接字符串的煞笔对接都气死我了
    adoal
        24
    adoal  
       83 天前   ❤️ 2
    手拼 URL 不检查不转换参数偏要怪 base64 使用了不兼容 URL 的字符……那你手拼 SQL 直接传裸参试试。
    tool2dx
        25
    tool2dx  
       83 天前
    @lisongeee RFC 标准有,就是 https://en.wikipedia.org/wiki/Base64 里的 base64url ,简单来说就是把'+'和'/'变成'-'和'_', 再把尾巴=去掉。

    在国外项目挺常见的,比如 lets encrypt acme 协议提交的 url ,就必须这样编码。
    lisongeee
        26
    lisongeee  
       83 天前
    @tool2dx

    你的回复和我的回复没有一点关系,你是不是回复错人了

    ---

    另外 base64 尾部的 = 只是为了让这个 base64 string 的长度是 4 的倍数

    而现在的大多数解析器解析时都是支持忽略尾部 = 字符的,所以很多工具生成的 base64 都没有 = 字符
    tool2dx
        27
    tool2dx  
       83 天前
    @lisongeee 嗯,我只是多一句嘴,回复确实没啥关系。
    DOLLOR
        28
    DOLLOR  
       83 天前   ❤️ 2
    先不说 base64 ,你拼 URL 的时候不用 encodeURIComponent()再包裹一遍吗?被坑了几次都还没这个意识吗?
    FengMubai
        29
    FengMubai  
       83 天前
    base16(hex): 变为原来的 2 倍
    base32: 1.6 倍
    base64: 1.333 倍
    base85: 1.25 倍

    base85 里有反斜线, base64 已经很折中了
    BugCry
        30
    BugCry  
       83 天前
    网站可以发出来看一下吗,我保证不做 sql 注入
    drymonfidelia
        31
    drymonfidelia  
    OP
       83 天前
    @vvhy
    @CEBBCAT
    @lisongeee
    @adoal
    @tool2dx
    @DOLLOR
    @BugCry 我又没说我在 url 里直接拼 base64 ,是我对接的很多人、接口都在往 url 、cookies 里直接拼接 base64 ,大厂也在这么干。例如你随便打开一个有接入 akamai bot manager 的网站,像 nike.com ,找到 _abck 这个 cookies ,第三段就是直接拼接的 base64
    drymonfidelia
        32
    drymonfidelia  
    OP
       83 天前
    @drymonfidelia 然后 cookies 就是 key=value 编码的,如果直接按=截断就会出现这个问题
    zpfhbyx
        33
    zpfhbyx  
       83 天前
    base64 urlsafe
    oamu
        34
    oamu  
       83 天前
    稍微了解下位运算也不至于说出 “码表长度是 2 的整数次方没有任何的加成。” 这种话。
    proxytoworld
        35
    proxytoworld  
       83 天前
    @drymonfidelia 你没对=做判断吗,=是最后出现的字符,而且最多两个,这分割没啥大问题啊
    tool2dx
        36
    tool2dx  
       83 天前
    @drymonfidelia 你标题有没写 cookies ,只说了 url 。

    cookies 内是允许用=符号的,虽然我也觉得解析起来会很奇怪,但浏览器确实是允许的。
    msg7086
        37
    msg7086  
       83 天前   ❤️ 1
    URL 不适合用 base64 所以有了 base64 的 url-safe 版本。
    Cookie 不是问题吧,本来就是按等号分割成两部分,第二部分有没有等号不影响使用。
    drymonfidelia
        38
    drymonfidelia  
    OP
       83 天前
    @msg7086 URL 一样是用=分为两部分,只是 cookies 分隔符是=和;,URL 是=和&,没有本质区别我就只写了一个
    @tool2dx
    NoOneNoBody
        39
    NoOneNoBody  
       83 天前   ❤️ 1
    既然都说的二进制,总应该知道 6 个 bit 就是 64 吧? 62 不能完整表示 6 个 bit 啊,36 表示 5 个 bit 又浪费了几个字符
    这些编码本来就早于 url 诞生的,你应该问的是为什么 url 标准不考虑兼容 base64

    base64 的重要思想是将 bytes 以可视、非控制字符表示,是 bytes 类型转无控制字符的 string 类型的最简单实现,压根就和 url 没什么关系
    cheng6563
        40
    cheng6563  
       83 天前
    有没有可能就是单纯的 base64 比 url 诞生的早?
    GeekGao
        41
    GeekGao  
       83 天前
    延续 40 楼的发言:
    Base64 最早是在 1993 年由 IETF (互联网工程任务组)定义的。当时,它作为 MIME (多用途互联网邮件扩展)协议的一部分被提出。
    在那之前,互联网主要用于电子邮件和文件传输,但 WWW ( World Wide Web )还处于非常早期的阶段。Base64 并不是专门为 WWW 发明的,但它恰好在 WWW 开始普及之际被标准化。这种巧合使得 Base64 成为 WWW 生态系统中的一个重要工具,为网页中的二进制数据传输提供了可靠的解决方案。
    n18255447846
        42
    n18255447846  
       83 天前   ❤️ 1
    1. base64 应用广泛
    2. 处理 url 需要 encode/decode
    Terminl
        43
    Terminl  
       82 天前
    @drymonfidelia 这个还真不是 base64 的锅,完全是你的个人技术问题,图片传输也用 base64,data:image/png;base64,这种多符号的你不是丢失的更多信息?
    iceheart
        44
    iceheart  
       82 天前 via Android
    k/v 解析只需要处理到第一个分隔符,
    有 bug 改就完了。
    qiumaoyuan
        45
    qiumaoyuan  
       82 天前
    我把内容一字不漏的复制给了 claude ,得出了和楼上各位差不多的回答。
    ShuWei
        46
    ShuWei  
       82 天前
    会不会是你直接拿=去分割 url 的做法本身就不合理呢
    yianing
        47
    yianing  
       82 天前
    base64 里有给 url 准备的编码方式,golang 里叫 URLEncoding ,标准的是 StdEncoding
    inza9hi
        48
    inza9hi  
       81 天前
    @lisongeee 这里比较坑的点是 url 可能被 encoding 多次。
    举例:A 服务返回一个 json ,里面有一个 url B 服务调用 A 服务,返回给客户端,这时候是否要
    lisongeee
        49
    lisongeee  
       80 天前   ❤️ 1
    @inza9hi

    只要按照使用标准序列化对象解析/构造 url ,无论服务调用多少次都不可能出现这种问题

    但是实际上很多程序员都是半吊子懒狗,都喜欢手动拼接/分割参数,这就容易导致解码/编码时破坏整个 url
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   1276 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 25ms · UTC 23:33 · PVG 07:33 · LAX 15:33 · JFK 18:33
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.