program tip

Python : base64 디코딩시 '잘못된 패딩'오류 무시

radiobox 2020. 9. 3. 08:15
반응형

Python : base64 디코딩시 '잘못된 패딩'오류 무시


패딩 오류가 있어도 바이너리로 다시 변환하려는 base64로 인코딩 된 데이터가 있습니다. 내가 사용한다면

base64.decodestring(b64_string)

'잘못된 패딩'오류가 발생합니다. 다른 방법이 있습니까?

업데이트 : 모든 피드백에 감사드립니다. 솔직히 말해서, 언급 된 모든 방법이 약간 안타 나거나 그리워서 openssl을 시도하기로 결정했습니다. 다음 명령이 효과가있었습니다.

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

다른 응답에서 언급했듯이 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


필요에 따라 패딩 만 추가하면됩니다. 그러나 마이클의 경고에 유의하십시오.

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

패딩 오류가 있으면 문자열이 손상되었음을 의미합니다. base64로 인코딩 된 문자열은 길이 4의 배수 여야합니다. 패딩 문자 ( =)를 직접 추가하여 문자열을 4의 배수로 만들 수 있지만 뭔가 잘못되지 않는 한 이미 포함되어 있어야합니다.


"잘못된 패딩"은 "누락 된 패딩"뿐만 아니라 (믿거 나 말거나) "잘못된 패딩"을 의미 할 수 있습니다.

제안 된 "패딩 추가"메소드가 작동하지 않으면 후행 바이트를 제거해보십시오.

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

업데이트 : 여백을 제거한 후에 패딩을 추가하거나 끝에서 불량 바이트를 제거하는 작업을 수행해야합니다. 그렇지 않으면 길이 계산이 잘못됩니다.

복구해야하는 데이터의 (짧은) 샘플을 보여 주 셨다면 좋은 생각이 될 것입니다. 질문을 수정하고의 결과를 복사 / 붙여 넣기하십시오 print repr(sample) .

업데이트 2 : 인코딩이 url-safe 방식으로 수행되었을 수 있습니다. 이 경우 데이터에서 마이너스 및 밑줄 문자를 볼 수 있으며 다음을 사용하여 디코딩 할 수 있어야합니다.base64.b64decode(strg, '-_')

데이터에서 마이너스 및 밑줄 문자는 볼 수 없지만 플러스 및 슬래시 문자는 볼 수 있다면 다른 문제가있는 것이므로 추가 패딩 또는 크랙 제거 트릭이 필요할 수 있습니다.

데이터에 마이너스, 밑줄, 플러스 및 슬래시가 하나도 표시되지 않으면 두 개의 대체 문자를 결정해야합니다. 그들은 [A-Za-z0-9]에없는 것입니다. 그런 다음 두 번째 인수에서 사용해야하는 순서를 확인하기 위해 실험해야합니다.base64.b64decode()

업데이트 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

사용하다

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'

>>> 

I don't have the rep to comment, but a nice thing to note is that (at least in Python 3.x) base64.b64decode will truncate any extra padding provided there is enough in the first place.

So, something like: b'abc=' works just as well as b'abc=='.

What this means is that you can just add the maximum number of padding characters that you would ever need—which is three (b'===')—and base64 will truncate any unnecessary ones.

Basically:

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

is cleaner than

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

Check the documentation of the data source you're trying to decode. Is it possible that you meant to use base64.urlsafe_b64decode(s) instead of base64.b64decode(s)? That's one reason you might have seen this error message.

Decode string s using a URL-safe alphabet, which substitutes - instead of + and _ instead of / in the standard Base64 alphabet.

This is for example the case for various Google APIs, like Google's Identity Toolkit and Gmail payloads.


Adding the padding is rather... fiddly. Here's the function I wrote with the help of the comments in this thread as well as the wiki page for base64 (it's surprisingly helpful) 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)

There are two ways to correct the input data described here, or, more specifically and in line with the OP, to make Python module base64's b64decode method able to process the input data to something without raising an un-caught exception:

  1. Append == to the end of the input data and call base64.b64decode(...)
  2. If that raises an exception, then

    i. Catch it via try/except,

    ii. (R?)Strip any = characters from the input data (N.B. this may not be necessary),

    iii. Append A== to the input data (A== through P== will work),

    iv. Call base64.b64decode(...) with those A==-appended input data

The result from Item 1. or Item 2. above will yield the desired result.

Caveats

This does not guarantee the decoded result will be what was originally encoded, but it will (sometimes?) give the OP enough to work with:

Even with corruption I want to get back to the binary because I can still get some useful info from the ASN.1 stream").

See What we know and Assumptions below.

TL;DR

From some quick tests of base64.b64decode(...)

  1. it appears that it ignores non-[A-Za-z0-9+/] characters; that includes ignoring =s unless they are the last character(s) in a parsed group of four, in which case the =s terminate the decoding (a=b=c=d= gives the same result as abc=, and a==b==c== gives the same result as ab==).

  2. It also appears that all characters appended are ignored after the point where base64.b64decode(...) terminates decoding e.g. from an = as the fourth in a group.

As noted in several comments above, there are either zero, or one, or two, =s of padding required at the end of input data for when the [number of parsed characters to that point modulo 4] value is 0, or 3, or 2, respectively. So, from items 3. and 4. above, appending two or more =s to the input data will correct any [Incorrect padding] problems in those cases.

HOWEVER, decoding cannot handle the case where the [total number of parsed characters modulo 4] is 1, because it takes a least two encoded characters to represent the first decoded byte in a group of three decoded bytes. In uncorrupted encoded input data, this [N modulo 4]=1 case never happens, but as the OP stated that characters may be missing, it could happen here. That is why simply appending =s will not always work, and why appending A== will work when appending == does not. N.B. Using [A] is all but arbitrary: it adds only cleared (zero) bits to the decoded, which may or not be correct, but then the object here is not correctness but completion by base64.b64decode(...) sans exceptions.

What we know from the OP and especially subsequent comments is

  • It is suspected that there are missing data (characters) in the Base64-encoded input data
  • The Base64 encoding uses the standard 64 place-values plus padding: A-Z; a-z; 0-9; +; /; = is padding. This is confirmed, or at least suggested, by the fact that openssl enc ... works.

Assumptions

  • The input data contain only 7-bit ASCII data
  • The only kind of corruption is missing encoded input data
  • The OP does not care about decoded output data at any point after that corresponding to any missing encoded input data

Github

Here is a wrapper to implement this solution:

https://github.com/drbitboy/missing_b64


Simply add additional characters like "=" or any other and make it a multiple of 4 before you try decoding the target string value. Something like;

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)

In case this error came from a web server: Try url encoding your post value. I was POSTing via "curl" and discovered I wasn't url-encoding my base64 value so characters like "+" were not escaped so the web server url-decode logic automatically ran url-decode and converted + to spaces.

"+" is a valid base64 character and perhaps the only character which gets mangled by an unexpected url-decode.


In my case I faced that error while parsing an email. I got the attachment as base64 string and extract it via re.search. Eventually there was a strange additional substring at the end.

dHJhaWxlcgo8PCAvU2l6ZSAxNSAvUm9vdCAxIDAgUiAvSW5mbyAyIDAgUgovSUQgWyhcMDAyXDMz
MHtPcFwyNTZbezU/VzheXDM0MXFcMzExKShcMDAyXDMzMHtPcFwyNTZbezU/VzheXDM0MXFcMzEx
KV0KPj4Kc3RhcnR4cmVmCjY3MDEKJSVFT0YK

--_=ic0008m4wtZ4TqBFd+sXC8--

When I deleted --_=ic0008m4wtZ4TqBFd+sXC8-- and strip the string then parsing was fixed up.

So my advise is make sure that you are decoding a correct base64 string.


You should use

base64.b64decode(b64_string, ' /')

By default, the altchars are '+/'.

참고URL : https://stackoverflow.com/questions/2941995/python-ignore-incorrect-padding-error-when-base64-decoding

반응형