|
一个阳光明媚的早晨,我坐在笔记本电脑前重构一些 C# 代码。一切都进行得很顺利,这将是富有成效的一天。然后我向常量字符串文字添加了太多等号,事情就爆发了。生产力没了。周日平静的重构已经过去了。就连太阳也决定躲到云层后面。
在花了 30 到 40 分钟试图弄清楚我做错了什么之后,我意识到那不是我。是微软。显然,我偶然发现了 Base64 解码函数中的一个古老错误。Convert.FromBase64String 自2003 年 .NET 1.1引入以来,这个错误肯定就存在了。哇!那是旧的。而且重现起来并不是很困难。干得好:
从技术上讲,这是非法的 Base64。合法版本是"abc=". 仅注意一个填充字符=。Base64 编码用一个 ASCII 字符表示二进制输入的每 6 位。这意味着 Base64 编码字符串中的每 4 个字符代表 3 个字节。当编码数据不是 3 字节的倍数时,Base64 编码器会添加填充字符,使 Base64 成为 4 字符的倍数。这将生成"abc="正确填充的 Base64 字符串。添加另一个=就会使其无效。
Base64"abc="解码为两个字节[105, 183]。这是对的。在末尾添加另一个填充字符不应真正改变编码值。这就像在句子末尾添加一个空格。是的,它就在那里,但它并没有改变句子的意思。但.NET 不这么认为。"abc=="解码为一个字节[109]。它不仅变短了,这很奇怪,因为我们让输入变长了。也变得不一样了。第一个字节从 105 变为 109。并且也没有抛出异常。添加另一个=,你就会得到一个例外。惊人的!
代码:
输出:
'abc=' -> [105, 183]
'abc==' -> [109] 真正令人惊奇的是,这么多年没有人发现这一点。或者它被发现了,但没有得到修复。Base64 在网络信息交换中非常重要。它随处可见。然而,.NET 多年来一直没有摆脱有缺陷的 Base64 解码器。
起初我不敢相信,并开始调查。我用谷歌搜索了一段时间,并没有找到太多东西。然后我在StackOverflow上发帖,但也没有得到太多运气。一旦我弄清楚发生了什么事,我什至必须回答我自己的问题。在 GitHub 上搜索了一段时间后,我偶然发现了2018 年 7 月在 .NET Core 中进行的修复。因此最新的 .NET Core 版本可以正确处理此问题并引发异常:
Unhandled Exception: System.FormatException: The input is not a valid Base-64 string as it contains a non-base 64 character, more than two padding characters, or an illegal character among the padding characters.
at System.Convert.FromBase64CharPtr(Char* inputPtr, Int32 inputLength)
at System.Convert.FromBase64String(String s)
at Program.DecodeAndPrint(String base64) in ./base64/Program.cs:line 13
at Program.Main() in ./base64/Program.cs:line 8 他们花了大约 15 年的时间才找到并解决这个问题。有趣的是,没有人真正尝试专门修复它。当他们重写代码以使其更快时发生了这种情况:
Convert.FromBase64() 有一个微妙的错误,即字符串末尾的第二个非法填充字符导致解码通过删除倒数第五个字符而“成功”。
我们在 .NetCore 2.1 中优化该 api 时无意中修复了此错误。添加测试来记录错误并确保我们不会倒退。 所以这个问题在 .NET Core 2.2 中得到了修复。但在当前最新版本的 .NET Framework 4.7.2 中它仍然存在问题。看起来它在Mono 中也被破坏了。
.NET 4.7.2 的解决方法是使用如下内容重新填充错误填充的字符串:
原文:https://detunized.net/posts/2019-03-06-base64-decoding-bug-that-is-present-in-all-version-of-.net/
|
上一篇:Azure DevOps(八)使用 Pipelines Build 编译 ASP.NET MVC 项目下一篇:.NET 6 中新的计时器 PeriodicTimer 使用
|