由于业务场景中使用了base64编码进行数据的处理,最近被它折腾的不轻,今天就来看看,都是哪里出了问题。

一、参与签名时,对base64编码结果处理不当

我们知道,base64编码是由大小写字母、数字和+/=三个特殊字符,合计65个字符构成,而=是作为末尾补位符使用,参与编码的字符是64个。

因此,base64编码结果存在等号(=),经过urlencode编码过后,等号会被处理成%3D

假定被base64编码的参数为note,其原始内容为:note=中国牛逼~,base64_encode的结果为:5Lit5Zu954mb6YC8fg==,经过urlencode处理的结果为:5Lit5Zu954mb6YC8fg%3D%3D,因此,我们的请求参数变成了:note=5Lit5Zu954mb6YC8fg%3D%3D,当我们约定note参与签名时,对方就可能得到3种不同的参与签名内容:

md5('note=中国牛逼~')

md5('note=5Lit5Zu954mb6YC8fg==')

md5('note=5Lit5Zu954mb6YC8fg%3D%3D')

一般来说,不刻意进行base64_decode处理,第一种不会出现。

但是第二种和第三种就比较容易混淆了,他们受到web服务器解析参数影响,有些web服务器对参数默认进行urldecode处理,有些却没有。

因此,假定我们参与签名的是urlencode的内容,即md5('note=5Lit5Zu954mb6YC8fg%3D%3D')时,默认进行urldecode的业务方,得到的内容却是note=5Lit5Zu954mb6YC8fg==,如果对方直接对内容进行生成签名,就会导致md5签名无法匹配,校验失败。

md5('note=5Lit5Zu954mb6YC8fg==')=142b4b3921701512f39e88a6bf9a97a3

md5('note=5Lit5Zu954mb6YC8fg%3D%3D')=fbb7df69af21293c8a280a4d1b4663af

如果一开始对接的时候测试内容note的base64_encode结果就带有等号,那对接时就会发现签名异常。

但还有一种情况,base64_encode在某种情况下,不需要补位处理,比如:

base64_encode('中国牛逼')=5Lit5Zu954mb6YC8

这时候,对接的时候无论web服务器是否默认进行urldecode处理,参与签名的内容都是note=5Lit5Zu954mb6YC8,签名是可以通过的。

嘿嘿~~ 埋下一颗隐雷,等着生产环境踩上去。

二、前端对移除补位数据解析异常

由于base64_encode的补位规则是可以预知的,因此,按理来说,直接移除末尾的补位等号,正常也是可以解码成功的。

但是,本人照着这种常规推理来实现功能的时候,又踩坑了!

这个坑要从网络上传播的一段js版本的base64_decode代码说起,下面这个是其中一份copy:

异常测试效果如下,移除末尾等号和不移除返回数据长度不一致,MD5有差异:

Base64.decode('5Lit5Zu954mb6YC8fg').length

7

Base64.decode('5Lit5Zu954mb6YC8fg==').length

5

md5(Base64.decode('5Lit5Zu954mb6YC8fg'))

"558bcf327d6e5c908927176e61ea3707"

md5(Base64.decode('5Lit5Zu954mb6YC8fg=='))

"7e903f42b3062d75aedd01c7d1a95375"

我们来看看代码:

decode:function(input)

{

varoutput="";

varchr1,chr2,chr3;

varenc1,enc2,enc3,enc4;

vari=0;

input=input.replace(/[^A-Za-z0-9\+\/\=]/g,"");

while(i

{

enc1=this._keyStr.indexOf(input.charAt(i++));

enc2=this._keyStr.indexOf(input.charAt(i++));

enc3=this._keyStr.indexOf(input.charAt(i++));

enc4=this._keyStr.indexOf(input.charAt(i++));

chr1=(enc1<<2)|(enc2>>4);

chr2=((enc2&15)<<4)|(enc3>>2);

chr3=((enc3&3)<<6)|enc4;

output=output+String.fromCharCode(chr1);

if(enc3!=64)//移除末尾补位等号导致进入此条件,多返回String.fromCharCode(0)

{

output=output+String.fromCharCode(chr2);

}

if(enc4!=64)//移除末尾补位等号导致进入此条件,多返回String.fromCharCode(0)

{

output=output+String.fromCharCode(chr3);

}

}// Whend

output=Base64._utf8_decode(output);

returnoutput;

}

异常原因是,移除末尾的补位等号之后,此类库的decode方法获取最后两位补位的时候,无法正常获取等号,导致最后补位返回了字符 String.fromCharCode(0)的结果,这个是一个肉眼空白字符,但是是有长度的

因此,此类库的decode方法需要修复问题的话,有两种解决方案:提前补充末尾等号

input='5Lit5Zu954mb6YC8fg'

"5Lit5Zu954mb6YC8fg"

input+='='.repeat(4-(input.length%4))

"5Lit5Zu954mb6YC8fg=="

处理结果移除末尾空白符

returnoutput.replace(/\0+$/,'')

至此,前端类库修复完成。

三、前端类库无修复条件(无限复制导致维护成本过高)

实际业务过程中,还可能遇到这样的情况,前端类库无限复制,导致修改成本过高,我们只能曲线救国,从后端去解决补位等号导致的签名隐患。

根据base64编码的原理分析,我们可以发现,只要数据长度为3的倍数,则不需要补位等号。

>>>base64_encode('a')

=>"YQ=="

>>>base64_encode('aa')

=>"YWE="

>>>base64_encode('aaa')#长度为3

=>"YWFh"

>>>base64_encode('aaaa')

=>"YWFhYQ=="

>>>base64_encode('aaaaa')

=>"YWFhYWE="

>>>base64_encode('aaaaaa')#长度为6

=>"YWFhYWFh"

因此,我们的目标就是:让数据的长度扩充成3的倍数。

注意:

对于字符串来说,修改数据的长度可能会破坏数据本身,因此大家需要根据自身的业务场景来决定,切勿盲目修改。

对于数组来说,如果为其增加一个补位数据,不会破坏数据的使用,则可以通过此方式处理优化。

下面举例说明下针对数组的处理,从输出可以看出,当数组json后的数据长度不是3的倍数时,数组会追加一个补位数据_pad_,然后通过给他赋值补位,实现数组json后的数据长度满足3的倍数,从而得到最终无需补位末尾等号的base64_encode结果。

functionautoFixArrayBase64Pad($arr)

{

$jsonOrig=$jsonStr=json_encode($arr);

$jsonLen=strlen($jsonStr);

if($jsonLen%3!=0){

//加入填充数组

$arr['_pad_']='';

$jsonLen=strlen(json_encode($arr));

//补齐缺失长度

$autoPadLen=(3-($jsonLen%3))%3;

$arr['_pad_']=str_repeat('_',$autoPadLen);

$jsonStr=json_encode($arr);

}

echo"jsonOrig: ".$jsonOrig.", len: ".strlen($jsonOrig)."\njsonFix : ".$jsonStr

.", len: ".strlen($jsonStr)."\nbase64Orig: ".base64_encode($jsonOrig)

."\nbase64Fix : ".base64_encode($jsonStr)."\n\n";

returnbase64_encode($jsonStr);

}

for($i=1;$i<=3;$i++){

autoFixArrayBase64Pad(['name'=>str_repeat('a',$i)]);

}

for($i=1;$i<=3;$i++){

autoFixArrayBase64Pad([str_repeat('a',$i)]);

}

# 输出

jsonOrig:{"name":"a"},len:12

jsonFix:{"name":"a"},len:12

base64Orig:eyJuYW1lIjoiYSJ9

base64Fix:eyJuYW1lIjoiYSJ9

jsonOrig:{"name":"aa"},len:13

jsonFix:{"name":"aa","_pad_":""},len:24

base64Orig:eyJuYW1lIjoiYWEifQ==

base64Fix:eyJuYW1lIjoiYWEiLCJfcGFkXyI6IiJ9

jsonOrig:{"name":"aaa"},len:14

jsonFix:{"name":"aaa","_pad_":"__"},len:27

base64Orig:eyJuYW1lIjoiYWFhIn0=

base64Fix:eyJuYW1lIjoiYWFhIiwiX3BhZF8iOiJfXyJ9

jsonOrig:["a"],len:5

jsonFix:{"0":"a","_pad_":"_"},len:21

base64Orig:WyJhIl0=

base64Fix:eyIwIjoiYSIsIl9wYWRfIjoiXyJ9

jsonOrig:["aa"],len:6

jsonFix:["aa"],len:6

base64Orig:WyJhYSJd

base64Fix:WyJhYSJd

jsonOrig:["aaa"],len:7

jsonFix:{"0":"aaa","_pad_":"__"},len:24

base64Orig:WyJhYWEiXQ==

base64Fix:eyIwIjoiYWFhIiwiX3BhZF8iOiJfXyJ9

参考资料:未经同意禁止转载!

base64 string类 放不下_base64编码处理数据踩过的坑相关推荐

  1. base64 string类 放不下_千夜空的推荐 | LOFTER(乐乎) - 让兴趣,更有趣

    同人的含义 同人:同一个世界,同一群人 同人作者都拥有将所有的意难平抹消的能力,作者用心血改写了那个"憾"字,因此他们不用"死"来证明对彼此的不渝,不必用&qu ...

  2. linux系统下安装elasticsearch集群踩过的坑

    公司之前用的solr发现不太好用准备替换成es于是乎开始大刀阔斧的更换搜索服务器,本着有新版本不用旧版本的原则,所以刚搭好的基础环境,又得重新部署一遍.(重要的事记得说三遍!!! 记笔记真的很重要,好 ...

  3. c++ string类_C++|细说STL string类概貌及底层细节

    C语言中的字符串称为C风格字符串,是一个以'0'结尾的字符数组,string.h库只提供了有限.不甚安全的字符串操作函数.char str[]只能定义编译期确定大小的字符串,而保存在堆内存的动态字符数 ...

  4. base64 string 放不下_用 Base64 编码插入 Markdown 图片

    本文写于 2017年02月20日,距今已超过 1 年,距 2020年07月25日 的最后一次修改也已超过 3 个月,部分内容可能已经过时,您可以按需阅读.如果图片无法显示或者下载链接失效,请给我反馈, ...

  5. 【c++】string类的模拟实现(下)

    昨天学习了string类的基本实现,今天学习了完整的简单版实现,特来社区记录. 目录 前言 一.增 1.reserve扩容 2.push_back()插入 3.append()插入 4.+=重载 二. ...

  6. java base64 转图片不现实_BASE64编码的图片在网页中的显示问题的解决

    1.为什么要用到BASE64编码的图片信息 Base64是网络上最常见的用于传输8Bit字节代码的编码方式之一.Base64 主要不是加密,它主要的用途是把一些二进制数转成普通字符用于网络传输.由于一 ...

  7. 解读java.lang包下String类的API(一)

    说明: 继承自Object,实现了java.io.Serializable.Comparable.CharSequence接口 String类代表字符串,字符串是常量,他们的值创建之后不能更改.字符串 ...

  8. JavaSE——三个特殊的类:String类(下)

    String类(下) 1.字符串查找 从一个完整的字符串之中可以判断指定内容是否存在,查找方法如下: No. 方法名称 类型 描述 1. public boolean contains(CharSeq ...

  9. stream流、数学类、String类的常用方法、date、密码等

    一.API 1.基本概念 英文全称Application Programming Interface,翻译为"应用程序编程接口". 是一些预先定义的函数,目的是提供应用程序与开发人 ...

最新文章

  1. luogu4407 [JSOI2009]电子字典 字符串hash + hash表
  2. 【报错笔记】eclipse运行tomcat程序时报错
  3. Java 7和Java 8之间的细微自动关闭合同更改
  4. REVERSE-COMPETITION-GeekChallenge2021
  5. 云原生数据库如何打造业务弹性
  6. 潮流趋势UI素材|梯度半透明、透明套件
  7. 【总结】 Lucas定理
  8. div超出部分点点显示
  9. 《JavaScript高级程序设计》- 第一章:介绍JavaScript
  10. 最新老男孩Linux云计算sre学什么
  11. C++ Reference: Standard C++ Library reference: C Library: cwctype: wint_t
  12. 【分享】如何写出好的品牌故事?
  13. OneZero第一次会议(非正式)
  14. Python 网络爬虫及数据可视化
  15. [Android][sensor][mag]指南针方向偏差,软磁三轴调整
  16. 【毕业设计_课程设计】基于移动设备的眼球追踪技术及其应用(源码+论文)
  17. win8运行matlab7.0,Win8.1系统中matlab7.0不兼容的解决方法
  18. Ol4中晕圈点效果的实现
  19. 基于STM32F103移植华为LiteOS物联网系统
  20. 记Google应用内商品(消耗品)支付小坑

热门文章

  1. http状态码之304
  2. 关于java NIO中 Chennal的阻塞在regist方法上的解决办法
  3. 我一天的时间是怎么安排的?
  4. 《啊哈!算法》——快速排序
  5. vr全景拍摄的具体方法
  6. 深度学习基础系列(十一)| Keras中图像增强技术详解
  7. 楚留香手游系统互通的服务器,楚留香安卓和ios版数据互通吗 如何区别楚留香官方渠道...
  8. 如何发那种android领取比较麻烦的QQ口令红包?
  9. ECPipeline 用于纠删码的流水线修复
  10. 一个快捷隐藏的方法隐藏一列不用鼠标右键隐藏就可完成