Python:在base64解码时忽略'不正确的填充'错误

时间:2010-05-31 07:27:15

标签: python base64

我有一些base64编码的数据,即使其中存在填充错误,我也希望将其转换回二进制文件。如果我使用

base64.decodestring(b64_string)

它引发了“填充不正确”错误。还有另一种方式吗?

更新:感谢您的所有反馈。说实话,所提到的所有方法听起来都有点受欢迎 并且想念所以我决定尝试openssl。以下命令有效:

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

19 个答案:

答案 0 :(得分:70)

正如其他回复中所述,base64数据有多种方式可以被破坏。

但是,正如Wikipedia所述,删除填充(base64编码数据末尾的'='字符)是“无损”的:

  

从理论的角度来看,不需要填充字符,   因为可以从数字计算丢失的字节数   Base64位数。

因此,如果这对于你的base64数据来说真的是唯一“错误”,那么填充就可以被添加回来。我想出了这个能够解析WeasyPrint中的“数据”URL,其中一些是没有填充的base64:

import base64
import re

def decode_base64(data, altchars=b'+/'):
    """Decode base64, padding being optional.

    :param data: Base64 data as an ASCII byte string
    :returns: The decoded byte string.

    """
    data = re.sub(rb'[^a-zA-Z0-9%s]+' % altchars, b'', data)  # normalize
    missing_padding = len(data) % 4
    if missing_padding:
        data += b'='* (4 - missing_padding)
    return base64.b64decode(data, altchars)

测试此功能:weasyprint/tests/test_css.py#L68

答案 1 :(得分:29)

只需根据需要添加填充。但是,请注意迈克尔的警告。

b64_string += "=" * ((4 - len(b64_string) % 4) % 4) #ugh

答案 2 :(得分:23)

如果出现填充错误,则可能表示您的字符串已损坏; base64编码的字符串应具有四个长度的倍数。您可以尝试自己添加填充字符(=)以使字符串为四的倍数,但除非出现问题,否则它应该已经存在

答案 3 :(得分:21)

“填充不正确”不仅意味着“缺少填充”,还意味着(信不信由你)“不正确的填充”。

如果建议“添加填充”方法不起作用,请尝试删除一些尾随字节:

lens = len(strg)
lenx = lens - (lens % 4 if lens % 4 else 4)
try:
    result = base64.decodestring(strg[:lenx])
except etc

更新:在删除任何空格后,应该在添加填充或从末尾删除可能的坏字节时进行任何调整,否则长度计算将会被打乱。

如果您向我们展示了您需要恢复的(短)数据样本,那将是一个好主意。修改您的问题并复制/粘贴 print repr(sample)的结果。

更新2:编码可能是以网址安全的方式完成的。如果是这种情况,您将能够在数据中看到减号和下划线字符,并且您应该能够使用base64.b64decode(strg, '-_')

对其进行解码

如果您在数据中看不到减号和下划线字符,但可以看到加号和斜线字符,那么您还有其他一些问题,可能需要添加填充或删除 - 删除技巧。

如果您在数据中看不到减号,下划线,加号​​和斜线,则需要确定两个备用字符;他们将是那些不在[A-Za-z0-9]中的人。那么你需要尝试在base64.b64decode()

的第二个arg中查看他们需要使用哪个顺序

更新3 :如果您的数据是“公司机密”:
(a)你应该在前面说出来 (b)我们可以探索理解问题的其他途径,这很可能与编码字母表中使用的字符而不是+/或其他格式或无关字符有关

其中一种方法是检查数据中的非“标准”字符,例如

from collections import defaultdict
d = defaultdict(int)
import string
s = set(string.ascii_letters + string.digits)
for c in your_data:
   if c not in s:
      d[c] += 1
print d

答案 4 :(得分:16)

使用

string += '=' * (-len(string) % 4)  # restore stripped '='s

信用在这里的某处发表评论。

>>> import base64

>>> enc = base64.b64encode('1')

>>> enc
>>> 'MQ=='

>>> base64.b64decode(enc)
>>> '1'

>>> enc = enc.rstrip('=')

>>> enc
>>> 'MQ'

>>> base64.b64decode(enc)
...
TypeError: Incorrect padding

>>> base64.b64decode(enc + '=' * (-len(enc) % 4))
>>> '1'

>>> 

答案 5 :(得分:12)

我没有代表发表评论,但值得注意的是(至少在Python 3.x中)base64.b64decode将截断任何额外的填充,只要首先有足够的填充。< / p>

因此,b'abc='之类的内容与b'abc=='一样。

这意味着你可以只添加你需要的最大填充字符数 - 三个(b'===') - 而base64会截断任何不必要的填充字符。

基本上:

base64.b64decode(s + b'===')

更清洁
base64.b64decode(s + b'=' * (-len(s) % 4))

答案 6 :(得分:4)

查看您尝试解码的数据源的文档。您是否有可能使用base64.urlsafe_b64decode(s)代替base64.b64decode(s)?这可能是您看到此错误消息的原因之一。

  

使用URL替换字符串来解码字符串s,而不是替换字符串   +和_而不是标准Base64字母表中的/。

例如Google的各种Google API,例如Google的Identity Toolkit和Gmail有效负载。

答案 7 :(得分:4)

由于不正确的填充错误,因为有时在编码的字符串中也存在元数据 如果您的字符串如下所示:'data:image / png; base64,... base 64的东西....' 那么您需要先删除第一部分,然后再对其进行解码。

如果您有图像base64编码的字符串,请尝试下面的代码段。

from PIL import Image
from io import BytesIO
from base64 import b64decode
imagestr = 'data:image/png;base64,...base 64 stuff....'
im = Image.open(BytesIO(b64decode(imagestr.split(',')[1])))
im.save("image.png")

答案 8 :(得分:3)

如果您要解码网络图像,则只需使用base64.urlsafe_b64decode(data)。它将自动处理填充。

答案 9 :(得分:1)

添加填充是相当......繁琐。这是我在这个帖子中的评论以及base64的wiki页面(它非常有帮助)https://en.wikipedia.org/wiki/Base64#Padding的帮助下编写的函数。

import logging
import base64
def base64_decode(s):
    """Add missing padding to string and return the decoded base64 string."""
    log = logging.getLogger()
    s = str(s).strip()
    try:
        return base64.b64decode(s)
    except TypeError:
        padding = len(s) % 4
        if padding == 1:
            log.error("Invalid base64 string: {}".format(s))
            return ''
        elif padding == 2:
            s += b'=='
        elif padding == 3:
            s += b'='
        return base64.b64decode(s)

答案 10 :(得分:1)

我在不使用base64的情况下遇到了此错误。所以我得到了一个解决方案,该错误是 localhost 中的问题,它在 127.0.0.1

上可以正常工作

答案 11 :(得分:0)

在我的例子中,Gmail Web API 将电子邮件内容作为 base64 编码字符串返回,但它不是用标准 base64 字符/字母编码,而是用 base64 的“网络安全”字符/字母变体编码。 +/ 字符被替换为 -_。对于 python 3,使用 base64.urlsafe_b64decode()

答案 12 :(得分:0)

我也遇到了这个问题,但没有任何效果。 我终于设法找到了适合我的解决方案。我在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)   

根据此答案Trailing As in base64,原因为空。但是我仍然不知道为什么编码器会把它弄乱了……

答案 13 :(得分:0)

有两种方法可以更正此处描述的输入数据,或更具体地说,与OP保持一致,以使Python模块base64的b64decode方法能够将输入数据处理为 something 而不会提高的未捕获的异常:

  1. 追加==于输入数据和呼叫base64.b64decode的端部(...)
  2. 如果该引发了异常,然后

    i。通过尝试捕获它/除外,

    ii。 (R?)从输入数据中去除任何=字符(可能不需要),

    iii。追加A ==到输入数据(A ==至P ==将工作),

    iv。呼叫base64.b64decode(...)与A == - 所附输入数据

从第1项或第2项。以上结果将产生所需的结果。

注意事项

这不能保证解码后的结果将是原始编码的结果,但是(有时?)它会给OP提供足够的处理能力:

  

即使有腐败我要回去二进制,因为我仍然可以从ASN.1流“一些有用的信息)。

请参阅我们知道什么的和的假设的下面。

TL; DR

一些对base64.b64decode(...)的快速测试

  1. 似乎忽略了非[A-Za-z0-9 + /]字符;包括忽略= s ,除非,它们是四个解析后的组中的最后一个字符,在这种情况下,= s终止解码(a = b = c = d =给出相同的结果为abc =,和一个== b ==ç==给出相同的结果,AB ==)。

  2. 看来,在base64.b64decode(...)终止解码后,例如,所有字符会被忽略。从=作为一组的第四位。

如上面的几条评论所述,当[解析到该点的字符数为4的值]的值为0时,在输入数据的末尾需要填充为零或一或两个。或3或2。因此,从以上第3项和第4项开始,在输入数据中附加两个或多个=可以纠正在这种情况下的所有[Invalid padding]问题。

但是,解码无法处理[解析的字符模4总数]为1的情况,因为它需要至少两个编码字符来表示三个一组中的第一个解码字节解码字节。在 un 损坏的编码输入数据中,永远不会发生这种[N模4] = 1情况,但是由于OP指出字符可能丢失,因此可能会在这里发生。这就是为什么简单地附加= s并不总是有效的原因,以及为什么在附加==时不附加 A ==的原因。 N.B.使用[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

答案 14 :(得分:0)

你应该使用

traceResponse

默认情况下,altchars为base64.b64decode(b64_string, ' /')

答案 15 :(得分:0)

在我的情况下,我在解析电子邮件时遇到了这个错误。我将附件作为base64字符串并通过re.search提取它。最终,最后有一个奇怪的附加子串。

dHJhaWxlcgo8PCAvU2l6ZSAxNSAvUm9vdCAxIDAgUiAvSW5mbyAyIDAgUgovSUQgWyhcMDAyXDMz
MHtPcFwyNTZbezU/VzheXDM0MXFcMzExKShcMDAyXDMzMHtPcFwyNTZbezU/VzheXDM0MXFcMzEx
KV0KPj4Kc3RhcnR4cmVmCjY3MDEKJSVFT0YK

--_=ic0008m4wtZ4TqBFd+sXC8--

当我删除--_=ic0008m4wtZ4TqBFd+sXC8--并删除字符串时,解析已修复。

所以我的建议是确保你正在解码一个正确的base64字符串。

答案 16 :(得分:0)

如果此错误来自网络服务器:请尝试对您的帖子值进行网址编码。我通过“curl”进行POST,发现我没有对我的base64值进行url编码,因此像“+”这样的字符没有被转义,因此web服务器url-decode逻辑自动运行url-decode并将+转换为空格。

“+”是一个有效的base64字符,也许是唯一一个因意外的url-decode而受到损坏的字符。

答案 17 :(得分: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)

答案 18 :(得分:-3)

就我而言,在删除特定项目的 venv 并显示每个字段的错误后,我遇到了这个错误,所以我尝试通过更改 BROWSER(Chrome 到 Edge),实际上它起作用了..