Python: 在 base64 解码时忽略 'Incorrect padding' 错误

195

我有一些经过Base64编码的数据,即使其中存在填充错误,我也想将其转换回二进制。如果我使用

base64.decodestring(b64_string)

它引发了一个“填充不正确”的错误。还有其他方法吗?

更新:感谢所有的反馈。老实说,提到的所有方法听起来都有点靠运气,所以我决定尝试openssl。以下命令非常有效:

openssl enc -d -base64 -in b64string -out binary_data

8
你是否尝试过使用base64.b64decode(strg, '-_')?在你没有提供任何示例数据的情况下,这是解决你问题最有可能的Python方法。之前提出的"方法"只是调试建议,鉴于提供的信息很少,它们必然是"靠瞎猜"的。 - John Machin
4
@John Machin: 是的,我确实尝试了你的方法,但没有成功。这些数据是公司机密。 - FunLovinCoder
7
尝试运行 base64.urlsafe_b64decode(s)。该函数用于解码使用URL安全字符集编码的Base64字符串。 - Daniel F
1
请问您可以提供此代码的输出结果吗?sorted(list(set(b64_string)))。通过此方式,我们可以知道用于编码原始数据的字符集,进而提供一个更加准确的解决方案,同时不会泄露任何公司机密信息。 - Brian Carcich
1
是的,我知道这个问题已经解决了,但说实话,openssl 的解决方案对我来说也有些靠运气。 - Brian Carcich
显示剩余2条评论
22个回答

3
这里有两种方法可以纠正输入数据,或者更具体地说,符合OP要求,使Python模块base64的b64decode方法能够处理输入数据到某个东西而不会引发未捕获的异常:
  1. 在输入数据末尾添加==并调用base64.b64decode(...)。
  2. 如果引发异常,则

    i. 通过try/except捕获它,

    ii. (R?)从输入数据中删除任何=字符(注意:这可能不是必需的),

    iii. 在输入数据中添加A==(A==到P==都可以),

    iv. 使用那些A==-附加的输入数据调用base64.b64decode(...)。

项目1或项目2的结果将产生所需的结果。 注意事项 这不能保证解码后的结果与最初编码的结果相同,但它将(有时?)为OP提供足够的工作:

即使出现损坏,我仍然希望回到二进制文件,因为我仍然可以从ASN.1流中获取一些有用的信息。")。

请参阅下面的我们所知道的假设

概括:

通过对 base64.b64decode(...) 的一些快速测试,看起来它会忽略非[A-Za-z0-9+/]字符;这包括忽略 =s,除非它们是在四个解析组的末尾字符,此时 =s 终止解码 (a=b=c=d= 与 abc= 的结果相同,a==b==c== 与 ab== 的结果相同)。

另外,所有附加的字符在 base64.b64decode(...) 终止解码后都被忽略,例如在第四个组中的 =。

正如上面的一些评论所指出的那样,当[到目前为止解析的字符数模4]值为0、3或2时,输入数据末尾需要填充零个、一个或两个"="。因此,从上述第3和第4项中可以得知,在这些情况下将两个或更多的"="附加到输入数据中将会纠正任何[不正确的填充]问题。
然而,解码无法处理[总解析字符数模4]为1的情况,因为至少需要两个编码字符来表示三个解码字节中的第一个解码字节。在未损坏的编码输入数据中,这种[N模4]=1的情况永远不会发生,但是由于OP声明可能缺少字符,因此它可能会发生。这就是为什么简单地添加"="并不总是有效,而添加"A"==将在添加"=="无效时起作用的原因。注意:使用[A]几乎是任意的:它只向解码后添加了清除(零)位,这可能是正确的,也可能不正确,但这里的目标不是正确性,而是通过base64.b64decode(...)完成补全,没有异常。

我们从OP和后续评论中所知道的是

  • 怀疑Base64编码输入数据中缺少数据(字符)
  • Base64编码使用标准的64个位置值加填充:A-Z;a-z;0-9;+;/;=表示填充。这一点已经得到证实,或者至少被暗示了,因为openssl enc ...可以工作。

假设

  • 输入数据仅包含7位ASCII数据
  • 唯一的损坏类型是缺少编码输入数据
  • OP在任何对应于任何缺失的编码输入数据之后的任何时候都不关心解码输出数据

Github

这里是一个包装器来实现这个解决方案:

https://github.com/drbitboy/missing_b64


3
在我的情况下,Gmail Web API 返回的邮件内容是一个base64编码的字符串,但是它不是使用标准的base64字符/字母表进行编码,而是使用“网络安全”的字符/字母表进行编码。加号(+)和斜线(/)字符被替换为破折号(-)和下划线(_)。对于Python 3,请使用base64.urlsafe_b64decode()

3
这可以在一行代码中完成 - 无需添加临时变量: b64decode(f"{s}{'=' * (4 - len(s) % 4)}")

2

我在没有使用base64的情况下遇到了这个错误。所以我找到了一个解决方案,就是错误出现在本地主机上,但在127.0.0.1上运行良好。


3
这个回答似乎与问题无关。您能否详细解释一下问题所在以及它们之间的关系? - darclander
在使用谷歌浏览器运行Django应用程序时,我遇到了问题。通常情况下,Django应用程序在本地主机上运行。但是今天它在本地主机上无法运行,所以我不得不将“localhost”更改为“127.0.0.1”。现在它可以正常工作。而且,在不更改localhost的情况下,它也可以在其他浏览器(如Firefox)上运行。 - Nooras Fatima Ansari
2
非常奇怪,但这对我也起作用了 - 不确定为什么,但还是谢谢! - n1c9

1
def base64_decode(data: str) -> str:
    
    data = data.encode("ascii")

    rem = len(data) % 4

    if rem > 0:
        data += b"=" * (4 - rem)
    return base64.urlsafe_b64decode(data).decode('utf-8')

1
如果您想解释,请在您的答案中进行,而不是在评论中。 - General Grievance

1
如果这个错误来自于 web 服务器:尝试对你的 post 值进行 URL 编码。我是通过“curl”进行 POST,发现我没有对我的 base64 值进行 URL 编码,因此像“+”这样的字符没有被转义,所以 Web 服务器 URL 解码逻辑自动运行了 URL 解码并将“+”转换为空格。
“+”是一个有效的 base64 字符,也许是唯一一个会被意外的 URL 解码弄乱的字符。

1

您应该使用

base64.b64decode(b64_string, ' /')

默认情况下,altchars 为 '+/'

1
这在 Python 3.7 中不起作用。assert len(altchars) == 2, repr(altchars) - Dat TT

1
在我的情况下,我在解析电子邮件时遇到了这个错误。我得到了附件作为base64字符串,并通过re.search提取它。最终,在末尾有一个奇怪的额外子字符串。
dHJhaWxlcgo8PCAvU2l6ZSAxNSAvUm9vdCAxIDAgUiAvSW5mbyAyIDAgUgovSUQgWyhcMDAyXDMz
MHtPcFwyNTZbezU/VzheXDM0MXFcMzExKShcMDAyXDMzMHtPcFwyNTZbezU/VzheXDM0MXFcMzEx
KV0KPj4Kc3RhcnR4cmVmCjY3MDEKJSVFT0YK

--_=ic0008m4wtZ4TqBFd+sXC8--

当我删除了--_=ic0008m4wtZ4TqBFd+sXC8--并剥离字符串后,解析问题得到了修复。

因此,我的建议是确保您解码的是正确的base64字符串。


1
我也遇到了这个问题,但是什么都没用。
最终我找到了适合我的解决方案。我在base64中压缩了内容,并且在一百万条记录中出现了这种情况...
这是Simon Sapin建议的解决方案的一个版本。
如果填充缺失3,则删除最后3个字符。
不是"0gA1RD5L/9AUGtH9MzAwAAA=="
我们得到 "0gA1RD5L/9AUGtH9MzAwAA"
        missing_padding = len(data) % 4
        if missing_padding == 3:
            data = data[0:-3]
        elif missing_padding != 0:
            print ("Missing padding : " + str(missing_padding))
            data += '=' * (4 - missing_padding)
        data_decoded = base64.b64decode(data)   

根据这个答案 关于 base64 中的 Trailing As,原因是空值。但我仍然不知道为什么编码器会出错...

无法相信那起作用了,添加额外的“=”也没有用。我的结果是“T4NCg==”,无论添加或减少多少个“=”都没有任何区别,直到我删除了末尾的“g”。我注意到“g”!=“A”。 - rob

0

只需添加额外的字符,如“=”或其他任何字符,并使其成为4的倍数,然后再尝试解码目标字符串值。就像这样;

if len(value) % 4 != 0: #check if multiple of 4
    while len(value) % 4 != 0:
        value = value + "="
    req_str = base64.b64decode(value)
else:
    req_str = base64.b64decode(value)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接