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


111

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

base64.decodestring(b64_string)

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

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

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

5
실제로나요 TRY 사용 base64.b64decode(strg, '-_')? 샘플 데이터를 제공하지 않아도 문제에 대한 가장 가능성있는 Python 솔루션 인 선험적입니다. 제안 된 "방법"은 제공된 정보의 부족을 감안할 때 반드시 "히트 앤 미스"DEBUG 제안이었습니다.
John Machin

2
@John Machin : 예, TRY 귀하의 방법을 시도했지만 작동하지 않았습니다. 데이터는 회사 기밀입니다.
FunLovinCoder 2010-06-01

3
시도base64.urlsafe_b64decode(s)
Daniel F

이것의 출력을 제공 할 수 sorted(list(set(b64_string)))있습니까? 회사 기밀 정보를 공개하지 않고 원본 데이터를 인코딩하는 데 사용 된 문자를 공개해야하며, 이는 적중 또는 실패하지 않는 솔루션을 제공하기에 충분한 정보를 제공 할 수 있습니다.
Brian Carcich

예, 이미 해결되었음을 알고 있지만 솔직히 말해서 openssl 솔루션도 나에게 헛소리로 들립니다.
Brian Carcich

답변:


79

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


2
참고 : 유니 코드가 아닌 ASCII이므로 안전을 위해 다음을 수행 할 수 있습니다.str(data)
MarkHu

4
이것은 하나의 경고와 함께 좋습니다. base64.decodestring은 더 이상 사용되지 않습니다. base64.b64_decode 사용
ariddell

2
@ariddell 주석을 명확히하기 위해 Py3 base64.decodestring에서는 더 이상 사용되지 base64.decodebytes않지만 버전 호환성을 위해 사용하는 것이 더 좋습니다 base64.b64decode.
Cas

때문에 base64모듈은 입력에서 유효하지 않은 base64로 문자를 무시하지, 먼저해야 정상화 데이터를. 문자, 숫자 아니에요 아무것도 제거 /하거나 +, 및 다음 패딩을 추가합니다.
Martijn Pieters

39

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

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

1
확실히 0에서 0, 2에서 1, 1에서 2로 매핑되는 더 간단한 것이 있습니다.
badp

2
4 대신 3의 배수로 확장하는 이유는 무엇입니까?
Michael Mrozek

이것이 base64에 대한 wikipedia 기사가 암시하는 것처럼 보입니다.
badp

1
@bp : base64 인코딩에서 각 24 비트 (3 바이트) 이진 입력은 4 바이트 출력으로 인코딩됩니다. output_len % 3은 의미가 없습니다.
John Machin

8
추가 만하면 ===항상 작동합니다. 추가 =문자는 Python에 의해 겉보기에 안전하게 버려집니다.
Acumenus

32

디코딩하기 전에 바이트에 패딩을 추가해야하는 것 같습니다. 이 질문에 대한 다른 많은 답변이 있지만 (적어도 Python 3.x에서는) base64.b64decode처음에 충분한 패딩이 있으면 여분의 패딩을자를 것임을 지적하고 싶습니다 .

따라서 다음과 같이 b'abc='작동합니다 b'abc=='( b'abc=====').

이것이 의미하는 바는 필요한 최대 패딩 문자 수 (3 ( b'===')) 만 추가 할 수 있으며 base64는 불필요한 문자를 자릅니다.

이렇게하면 다음과 같이 작성할 수 있습니다.

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

다음보다 간단합니다.

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

1
좋아요. 너무 "추악한"것은 아닙니다. 감사합니다. :) 그런데 2 개 이상의 패딩 문자가 필요하지 않다고 생각합니다. Base64 알고리즘은 한 번에 3 개의 문자 그룹에서 작동하며 마지막 문자 그룹의 길이가 1 ~ 2 자일 때만 패딩이 필요합니다.
Otto

@Otto 여기서 패딩은 디코딩을위한 것이며 4 개의 문자 그룹에서 작동합니다. Base64로는 인코딩 : 3 개 문자의 그룹에 작품을
헨리 우디

그러나 인코딩하는 동안 최대 2 개가 추가되어 나중에 "손실"되어 디코딩 전에 다시 추가해야한다는 것을 알고 있다면 디코딩하는 동안에도 최대 2 개만 추가하면됩니다. #ChristmasTimeArgumentForTheFunOfIt
Otto

@Otto 나는 당신이 옳다고 믿습니다. 예를 들어 길이가 5 인 base64로 인코딩 된 문자열에는 3 개의 패딩 문자가 필요하지만 길이가 5 인 문자열은 base64로 인코딩 된 문자열에 대해 유효한 길이도 아닙니다. 오류가 발생 binascii.Error: Invalid base64-encoded string: number of data characters (5) cannot be 1 more than a multiple of 4합니다.. 지적 해 주셔서 감사합니다!
Henry Woody

24

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

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

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

데이터는 표준 base64 문자 집합으로 구성됩니다. 1 개 이상의 문자가 누락 되었기 때문에 문제가 발생한다고 확신합니다. 따라서 패딩 오류입니다. Python에 강력한 솔루션이 없으면 openssl을 호출하는 솔루션을 사용하겠습니다.
FunLovinCoder 2010 년

1
오류를 조용히 무시하는 "솔루션"은 "강력한"이라는 용어를 사용할 가치가 거의 없습니다. 앞서 언급했듯이 다양한 Python 제안은 문제가 무엇인지 알아 내고 PRINCIPLED 솔루션을 준비하기위한 디버깅 방법이었습니다.
John Machin

7
내 요구 사항은 base64가 손상된 이유에 대한 문제를 해결하는 것이 아니라 내가 제어 할 수없는 소스에서 비롯된 것입니다. 내 요구 사항은 손상된 경우에도받은 데이터에 대한 정보를 제공하는 것입니다. 이를 수행하는 한 가지 방법은 기본 ASN.1에서 정보를 수집 할 수 있도록 손상된 base64에서 이진 데이터를 가져 오는 것입니다. 흐름. 손상된 base64를 디버깅하는 방법과 같은 다른 질문에 대한 답변이 아닌 해당 질문에 대한 답변을 원했기 때문에 원래 질문을했습니다.
FunLovinCoder 2010 년

그냥 정상화 Base64로 문자가 아닌 문자열, 제거 아무것도. 시작이나 끝이 아닌 어디에서나.
Martijn Pieters

24

사용하다

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'

>>> 

4
그는이 코멘트를 의미합니다 : stackoverflow.com/questions/2941995/…
jackyalcine

22

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


기본 이진 데이터는 ASN.1입니다. 손상이 있어도 ASN.1 스트림에서 유용한 정보를 얻을 수 있기 때문에 바이너리로 돌아가고 싶습니다.
FunLovinCoder

사실이 아닙니다. 보안 검사를 위해 jwt를 디코딩하려면 필요합니다
DAG

4

디코딩하려는 데이터 소스의 설명서를 확인하십시오. base64.urlsafe_b64decode(s)대신 사용하려는 것이 가능 base64.b64decode(s)합니까? 이것이이 오류 메시지를 본 이유 중 하나입니다.

표준 Base64 알파벳에서 / 대신 + 및 _ 대신-를 대체하는 URL 안전 알파벳을 사용하여 문자열 s를 디코딩합니다.

예를 들어 Google의 Identity Toolkit 및 Gmail 페이로드와 같은 다양한 Google API의 경우입니다.


1
이것은 질문에 전혀 대답하지 않습니다. 또한 urlsafe_b64decode패딩도 필요합니다.
rdb

글쎄요,이 질문에 답하기 전에 제가 가지고 있던 문제가 있었는데, 이는 Google의 Identity Toolkit과 관련이 있습니다. 패딩이 올바른 것처럼 보이더라도 잘못된 패딩 오류가 발생했습니다 (서버에 있다고 생각합니다). 을 사용해야한다는 것이 밝혀졌습니다 base64.urlsafe_b64decode.
Daniel F

나는 그것이 rdb라는 질문에 대한 답이 아니라는 데 동의하지만, 내가들을 필요가있는 것이기도합니다. 나는 조금 더 좋은 어조로 대답을 바 꾸었습니다. 다니엘, 이것이 당신에게 효과가 있기를 바랍니다.
Henrik Heimbuerger

완벽합니다. 나는 그것이 다소 불친절하게 들리는 것을 알아 차리지 못했고, 그것이 문제를 해결한다면 가장 빠른 해결책이 될 것이라고 생각했고, 그런 이유로 가장 먼저 시도해야 할 것입니다. 변경해 주셔서 감사합니다. 환영합니다.
Daniel F

이 답변은 JWT에서 파생 된 Google 액세스 토큰을 디코딩하는 내 문제를 해결했습니다. 다른 모든 시도는 "잘못된 패딩"으로 이어졌습니다.
John Hanley

2

패딩을 추가하는 것은 오히려 ... 어리석은 일입니다. 이 스레드의 주석과 base64 용 위키 페이지 (놀랍게도 도움이 됨) 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)

2

base64.urlsafe_b64decode(data)웹 이미지를 디코딩하려는 경우 간단히 사용할 수 있습니다 . 패딩을 자동으로 처리합니다.


정말 도움이됩니다!
Moon

1

입력 데이터의 입력 데이터를 처리 할 파이썬 모듈 base64로의 b64decode 방법은 수 있도록, 더 구체적으로하고, 영업 이익과 일치, 여기에 설명 또는 수정하는 방법은 두 가지가 있습니다 뭔가를 미 잡은 예외를 발생시키지 않고는 :

  1. ==를 입력 데이터 끝에 추가하고 base64.b64decode (...)를 호출합니다.
  2. 예외가 발생하면

    나는. try / except를 통해 잡아라.

    ii. (R?) 입력 데이터에서 모든 = 문자를 제거합니다 (필요하지 않을 수도 있음).

    iii. A ==를 입력 데이터에 추가합니다 (A ==에서 P ==까지 작동 함).

    iv. A ==가 추가 된 입력 데이터로 base64.b64decode (...)를 호출합니다.

위의 항목 1 또는 항목 2의 결과는 원하는 결과를 산출합니다.

주의 사항

이것은 디코딩 된 결과가 원래 인코딩 된 결과임을 보장하지는 않지만 (때때로?) 작업에 충분한 OP를 제공합니다.

손상이 있더라도 ASN.1 스트림에서 유용한 정보를 얻을 수 있기 때문에 바이너리로 돌아가고 싶습니다. ").

를 참조하십시오 우리가 알고있는 무엇가정 아래.

TL; DR

base64.b64decode (...)의 몇 가지 빠른 테스트에서

  1. 비 [A-Za-z0-9 + /] 문자를 무시하는 것으로 보입니다. 이는 구문 분석 된 4 개의 그룹에서 마지막 문자가 아닌 경우 = s를 무시하는 것을 포함합니다 .이 경우 = s는 디코딩을 종료합니다 (a = b = c = d =는 abc = 및 a =와 동일한 결과를 제공합니다. = b == c ==는 ab ==)와 동일한 결과를 제공합니다.

  2. 또한 추가 된 모든 문자 base64.b64decode (...)가 디코딩을 종료하는 지점 (예 : an =에서 그룹의 네 번째로) 이후 에 무시 되는 것으로 보입니다.

위의 여러 주석에서 언급했듯이 [모듈로 4 지점까지 구문 분석 된 문자 수] 값이 0 또는 3 일 때 입력 데이터 끝에 필요한 패딩이 0 또는 1 또는 2입니다. 또는 각각 2입니다. 따라서 위의 항목 3. 및 4.에서 입력 데이터에 = s를 두 개 이상 추가하면 이러한 경우 [잘못된 패딩] 문제가 해결됩니다.

그러나 디코딩은 3 개의 디코딩 된 바이트 그룹에서 첫 번째 디코딩 된 바이트를 나타 내기 위해 최소 2 개의 인코딩 된 문자가 필요하기 때문에 [모듈로 4의 구문 분석 된 총 문자 수]가 1 인 경우를 처리 할 수 ​​없습니다. 년 유엔 인코딩 된 입력 데이터 손상이 = 1의 경우가 발생하지 [N (4)은 모듈로, 그러나 문자가 누락 될 수 있음을 설명한 OP로서, 여기 일어날 수있다. 그렇기 때문에 단순히 = s를 추가하는 것이 항상 작동 하는 것은 아니며 ==를 추가 할 때 A ==를 추가해도 작동하지 않는 이유입니다. NB [A]를 사용하는 것은 거의 임의적입니다. 디코딩 된 부분에 지워진 (0) 비트 만 추가합니다. 이는 정확할 수도 있고 그렇지 않을 수도 있지만 여기에있는 객체는 정확성이 아니라 base64.b64decode (...)에 의한 완성입니다. 예외는 없습니다. .

OP 및 특히 후속 의견에서 우리가 알고 있는 것은

  • Base64로 인코딩 된 입력 데이터에 누락 된 데이터 (문자)가있는 것으로 의심됩니다.
  • Base64 인코딩은 표준 64 자리 값과 패딩을 사용합니다. AZ; az; 0-9; +; /; = 패딩입니다. 이것은 효과가 있다는 사실에 의해 확인되거나 적어도 제안됩니다 openssl enc ....

가정

  • 입력 데이터에는 7 비트 ASCII 데이터 만 포함됩니다.
  • 유일한 종류의 손상은 인코딩 된 입력 데이터가 누락되었습니다.
  • OP는 누락 된 인코딩 된 입력 데이터에 해당하는 이후의 어느 시점에서든 디코딩 된 출력 데이터를 신경 쓰지 않습니다.

Github

다음은이 솔루션을 구현하는 래퍼입니다.

https://github.com/drbitboy/missing_b64


1

잘못된 패딩 오류가 발생하는 이유는 가끔 인코딩 된 문자열에 메타 데이터도 있기 때문입니다. 문자열이 'data : image / png; base64, ... base 64 stuff ....'와 같은 경우 첫 번째 디코딩하기 전에 부분.

이미지 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")

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)

0

이 오류가 웹 서버에서 발생한 경우 : 게시물 값을 url 인코딩 해보십시오. "curl"을 통해 게시하고 있었고 "+"와 같은 문자가 이스케이프되지 않도록 base64 값을 url 인코딩하지 않았으므로 웹 서버 url-decode 논리가 자동으로 url-decode를 실행하고 +를 공백으로 변환했습니다.

"+"는 유효한 base64 문자이며 예상치 못한 URL 디코딩으로 인해 엉망이되는 유일한 문자 일 수 있습니다.


0

제 경우에는 이메일을 구문 분석하는 동안 오류가 발생했습니다. 첨부 파일을 base64 문자열로 받고 re.search를 통해 추출합니다. 결국 끝에 이상한 추가 하위 문자열이있었습니다.

dHJhaWxlcgo8PCAvU2l6ZSAxNSAvUm9vdCAxIDAgUiAvSW5mbyAyIDAgUgovSUQgWyhcMDAyXDMz
MHtPcFwyNTZbezU/VzheXDM0MXFcMzExKShcMDAyXDMzMHtPcFwyNTZbezU/VzheXDM0MXFcMzEx
KV0KPj4Kc3RhcnR4cmVmCjY3MDEKJSVFT0YK

--_=ic0008m4wtZ4TqBFd+sXC8--

--_=ic0008m4wtZ4TqBFd+sXC8--문자열을 삭제 하고 제거하면 구문 분석이 수정되었습니다.

따라서 내 조언은 올바른 base64 문자열을 디코딩하고 있는지 확인하는 것입니다.


0

당신은 사용해야합니다

base64.b64decode(b64_string, ' /')

기본적으로 altchars는 '+/'입니다.


1
파이썬 3.7에서는 작동하지 않습니다. 어설 LEN (altchars) == 2에 repr (altchars)
닷 TT

0

나는이 문제도 만났고 아무것도 작동하지 않았습니다. 마침내 저에게 맞는 솔루션을 찾았습니다. 나는 base64에 압축 된 내용을 가지고 있었고 이것은 백만개의 레코드 중 1 개에 일어났다.

이것은 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 의 이유는 null입니다. 하지만 왜 인코더가 이것을 엉망으로 만드는지 모르겠습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.