一个关于Base64的问题引发的回顾

前言

一次浏览支付宝社区的看到这么一个问题:小程序webview嵌套的h5用了 img src 是base64图片,域名也校验导致访问受限!

看得我一愣一愣的,印象中base64编码的图片不就已经是数据源了吗,怎么还会触发资源请求,还被访问受限了呢,难道我记错了?于是接下来对base64进行一波回顾

base64

什么是base64

百度百科中对Base64有一个很好的解释:Base64是网络上最常见的用于传输8Bit字节码的编码方式之一,Base64就是一种基于64个可打印字符来表示二进制数据的方法

要理解这句话就不得不了解一下base64对由来了

 

base64的由来

电子邮件刚出来的时候,只传递英文字符,这没有问题,但是后来,中国人,日本人都要发 email,这样问题就来了,因为这些字符有可能会被邮件服务器或者网关当成命令处理,故必须得有一种编码来对邮件进行加密,但是加密的目的是为了能够使得一些原始的服务器不出问题(新得牛叉服务器已经能处理这些乱七八糟得情况了,不过 因为已经形成了一套规范,所以邮件还是得经过 Base64 编码才能传递),这样加密必须得简单(那搞个取反,异或加密吧,:-),还是没解决根本问题 咯),加密简单,这样客户端程序加密解密也快,又要是明文 Ascii 编码,这样 Base64 就诞生了。随之,Base64在URL、Cookie、网页传输少量二进制文件中也有相应的使用

当初设计人员主要是考虑了两个问题

  • 加密算法复杂程度和效率

  • 如何处理传输

Base64 基本都能满足,如果因为发一封邮件把 CPU 占到 100%或者把内存给用完了,那就完全没必要了,编码之后只要普通人一眼看不出内容就行了

与此类似,网络上传输的字符并不全是可打印的字符,比如二进制文件、图片等。Base64的出现就是为了解决此问题,它是基于64个可打印的字符来表示二进制的数据的一种方法

 

可打印字符

什么是可打印字符,为什么要用它来传输8Bit字节码呢,在此之前我们先了解一下什么情况下需要使用到Base64,Base64一般用于在HTTP协议下传输二进制数据,由于HTTP协议是文本协议,所以在HTTP协议下传输二进制数据需要将二进制数据转换为字符数据。然而直接转换是不行的,因为网络传输只能传输可打印字符。什么是可打印字符?在ASCII码中规定,0~31、127这33个字符属于控制字符,32~126这95个字符属于可打印字符,也就是说网络传输只能传输这95个字符,不在这个范围内的字符无法传输,那么该怎么才能传输其他字符呢?其中一种方式就是使用Base64

 

Base64的编码原理

Base64编码之所以称为Base64,是因为其使用64个字符来对任意数据进行编码,同理有Base32、Base16编码。标准Base64编码使用的64个字符为

这64个字符是各种字符编码(比如ASCII编码)所使用字符的子集,基本,并且可打印。唯一有点特殊的是最后两个字符,因对最后两个字符的选择不同,Base64编码又有很多变种,比如Base64 URL编码

Base64编码本质上是一种将二进制数据转成文本数据的方案。对于非二进制数据,是先将其转换成二进制形式,然后每连续6比特(2的6次方=64)计算其十进制值,根据该值在上面的索引表中找到对应的字符,最终得到一个文本字符串

假设我们要对 Hello! 进行Base64编码,按照ASCII表,其转换过程如下图所示

可知 Hello! 的Base64编码结果为 SGVsbG8h ,原始字符串长度为6个字符,编码后长度为8个字符,每3个原始字符经Base64编码成4个字符,编码前后长度比4/3,这个长度比很重要 - 比原始字符串长度短,则需要使用更大的编码字符集,这并不我们想要的;长度比越大,则需要传输越多的字符,传输时间越长。Base64应用广泛的原因是在字符集大小与长度比之间取得一个较好的平衡,适用于各种场景

注:图表中蓝色背景的二进制0值是额外补充的

Hello!! Base64编码的结果为 SGVsbG8hIQAA 。最后2个零值只是为了Base64编码而补充的,在原始字符中并没有对应的字符,那么Base64编码结果中的最后两个字符 AA 实际不带有效信息,所以需要特殊处理,以免解码错误

标准Base64编码通常用 = 字符来替换最后的 A,即编码结果为 SGVsbG8hIQ==。因为 = 字符并不在Base64编码索引表中,其意义在于结束符号,在Base64解码时遇到 = 时即可知道一个Base64编码字符串结束

如果Base64编码字符串不会相互拼接再传输,那么最后的 = 也可以省略,解码时如果发现Base64编码字符串长度不能被4整除,则先补充 = 字符,再解码即可

解码是对编码的逆向操作,但注意一点:对于最后的两个 = 字符,转换成两个 A 字符,再转成对应的两个6比特二进制0值,接着转成原始字符之前,需要将最后的两个6比特二进制0值丢弃,因为它们实际上不携带有效信息

切忌误用

  • 可能会有人在不理解Base64编码的情况下,将其误用于数据加密或数据校验

    Base64是一种数据编码方式,目的是让数据符合传输协议的要求。标准Base64编码解码无需额外信息即完全可逆,即使你自己自定义字符集设计一种类Base64的编码方式用于数据加密,在多数场景下也较容易破解

    对于数据加密应该使用专门的目前还没有有效方式快速破解的加密算法。比如:对称加密算法AES-128-CBC,对称加密需要密钥,只要密钥没有泄露,通常难以破解;也可以使用非对称加密算法,如 RSA,利用极大整数因数分解的计算量极大这一特点,使得使用公钥加密的数据,只有使用私钥才能快速解密

    对于数据校验,也应该使用专门的消息认证码生成算法,如 HMAC - 一种使用单向散列函数构造消息认证码的方法,其过程是不可逆的、唯一确定的,并且使用密钥来生成认证码,其目的是防止数据在传输过程中被篡改或伪造。将原始数据与认证码一起传输,数据接收端将原始数据使用相同密钥和相同算法再次生成认证码,与原有认证码进行比对,校验数据的合法性

  • 那么针对各大网站被脱库的问题,应该怎么存储用户的登录密码呢

    答案是在注册时,根据用户设置的登录密码,生成其消息认证码,然后存储用户名和消息认证码,不存储原始密码。每次用户登录时,根据登录密码,生成消息认证码,与数据库中存储的消息认证码进行比对,以确认是否为有效用户,这样即使网站被脱库,用户的原始密码也不会泄露,不会为用户使用的其他网站带来账号风险

    当然,使用的消息认证码算法其哈希碰撞的概率应该极低才行,目前一般在HMAC算法中使用SHA256。对于这种方式需要注意一点:防止用户使用弱密码,否则也可能会被暴力破解。现在的网站一般要求用户密码6个字符以上,并且同时有数字和大小写字母,甚至要求有特殊字符

 

注意事项

  • 大多数编码都是由字符串转化成二进制的过程,而Base64的编码则是从二进制转换为字符串,与常规恰恰相反

  • Base64编码主要用在传输、存储、表示二进制领域,不能算得上加密,只是无法直接看到明文,也可以通过打乱Base64编码来进行加密

  • 中文有多种编码(比如:utf-8、gb2312、gbk等),不同编码对应Base64编码结果都不一样

 

延伸

上面我们已经看到了Base64就是用6位(2的6次幂就是64)表示字符,因此成为Base64,同理,Base32就是用5位,Base16就是用4位

 

总结

以上我们知道,如果我们把文件转换成Base64格式的,文件会明显的增大,那么是不是Base64对于我们来说就没有任何卵用呢

并不是,在图片处理上,有时候,我们把图片的引用从请求后端数据改成直接引用Base64数据,这样可以减少一个请求,对网站进行优化。说到减少请求就不得不是CSSspirite,那么针对这两个方案的选择该怎样呢

原则建议如下

  • 有特殊用途,无法和其他图片合在一起做成雪碧图的

  • 使用与小图片,因为打的图片换成Base64对于CSS体积增大明显

  • 更新需求不高(如果图片三天两头的更新,那要把前端累死了)

原文地址:https://www.cnblogs.com/zhazhanitian/p/14355258.html