Python:base64デコード時に「不正なパディング」エラーを無視する


111

埋め込みエラーがある場合でも、バイナリに変換したいbase64エンコードのデータがあります。私が使うなら

base64.decodestring(b64_string)

「不正なパディング」エラーが発生します。別の方法はありますか?

更新:すべてのフィードバックをありがとう。正直に言うと、言及されたすべての方法は少し不満に聞こえたので、opensslを試すことにしました。次のコマンドはごちそうを機能させました:

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

5
あなたが実際にしましたTRY使用してbase64.b64decode(strg, '-_')?これはアプリオリであり、サンプルデータを提供する手間をかけずに、問題に対する最も可能性の高いPythonソリューションです。提案された「方法」はデバッグの提案であり、提供された情報が不足しているため、必然的に「ヒットアンドミス」になります。
John Machin

2
@John Machin:はい、私はあなたの方法を試しましたが、うまくいきませんでした。データは会社の機密情報です。
FunLovinCoder

3
お試しくださいbase64.urlsafe_b64decode(s)
ダニエルF

これの出力を提供できますsorted(list(set(b64_string)))か?会社の機密を明らかにすることなく、元のデータをエンコードするために使用された文字が明らかになるはずです。これにより、ヒットまたはミスのないソリューションを提供するのに十分な情報が提供される可能性があります。
ブライアンカーチッチ

はい、私はそれが既に解決されていることを知っていますが、正直に言うと、opensslソリューションも私には当てはまります。
ブライアンカーチッチ

回答:


79

他の応答で述べたように、base64データが破損する可能性があるさまざまな方法があります。

ただし、ウィキペディアが言うように、パディング(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ではないUnicodeのため、安全のために、あなたがしたい場合がありますstr(data)
MarkHu

4
これは1つの注意点で問題ありません。base64.decodestringは非推奨です。base64.b64_decodeを使用してください
ariddell

2
@ariddellを明確にするために、コメントbase64.decodestringはPy3 では廃止さ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の倍数に拡張するのですか?
マイケルム​​ロゼック

それが、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文字のグループで機能します:)
Henry Woody

ただし、エンコード中に最大2が追加されることがわかっていて、後で「失われる」可能性があり、デコード前に再度追加する必要がある場合は、デコード中にも最大2を追加するだけでよいことがわかります。#ChristmasTimeArgumentForTheFunOfIt
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セーフな方法で行われた可能性があります。この場合、マイナス記号とアンダースコア文字がデータに表示され、次のコマンドを使用してデコードできるはずです。base64.b64decode(strg, '-_')

データにマイナス文字とアンダースコア文字は表示されないが、プラス文字とスラッシュ文字は表示される場合は、別の問題があり、追加パディングまたは削除クリフトトリックが必要になる可能性があります。

データにマイナス、アンダースコア、プラス、スラッシュがまったく表示されない場合は、2つの代替文字を特定する必要があります。それらは[A-Za-z0-9]にないものになります。次に、それらの2番目の引数で使用する必要がある順序を確認するために実験する必要がありますbase64.b64decode()

アップデート3:あなたのデータが「機密会社」である場合:
()あなたがそう言うべきアップフロント
(b)は、我々は文字がの代わりに使用しているものに関連している可能性が高い問題、理解する上で、他の道を探ることができます+し、/中にエンコーディングアルファベット、または他のフォーマットまたは無関係な文字。

そのような方法の1つは、データに「非標準」文字が含まれているかどうかを調べることです。たとえば、

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

1
エラーを静かに無視する「解決策」は、「ロバスト」という言葉に値することはほとんどありません。先に述べたように、さまざまなPythonの提案は、問題が何であるかを見つけるためのデバッグの方法であり、PRINCIPLEDソリューションの準備でした...そのようなことに興味はありませんか?
John Machin

7
私の要件は、base64が破損している理由の問題を解決することではありません-私が制御できないソースからのものです。私の要件は、破損している場合でも、受信したデータに関する情報を提供することです。これを行う1つの方法は、破損したbase64からバイナリデータを取得して、基になるASN.1から情報を収集できるようにすることです。ストリーム。元の質問をしたのは、破損したbase64をデバッグする方法など、別の質問に対する回答ではなく、その質問に対する回答が欲しいからです。
FunLovinCoder

ただ、正規化の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 2010年

真実ではありません。セキュリティチェックのためにjwtをデコードする場合は、それが必要になります
DAG

4

デコードしようとしているデータソースのドキュメントを確認してください。base64.urlsafe_b64decode(s)代わりに使用するつもりでしたbase64.b64decode(s)か?これが、このエラーメッセージが表示された理由の1つです。

標準のBase64アルファベットで+の代わりに-を、/の代わりに_を置換するURLセーフアルファベットを使用して文字列sをデコードします。

これは、たとえば、GoogleのIdentity ToolkitやGmailのペイロードなど、さまざまなGoogle APIの場合です。


1
これはまったく質問に答えません。さらに、urlsafe_b64decodeパディングも必要です。
rdb 2016

さて、この質問に答える前に、GoogleのIdentity Toolkitに関連する問題がありました。パディングが正しいように見えても、パディングエラーが正しく表示されませんでした(サーバーにあると思います)。私が使用しなければならなかったことが判明しましたbase64.urlsafe_b64decode
ダニエルF

rdbの質問には答えられないことに同意しますが、それはまさに私が聞く必要があったものでもありました。答えをもう少しいい口調に言い換えましたが、これがうまくいくことを願っています、ダニエル。
Henrik Heimbuerger

まったく問題ありません。少し不親切に聞こえることに気づかなかった。問題を修正できれば、それが最も迅速な修正であり、そのため、最初に試すべきことだと思った。変更をありがとう、大歓迎です。
ダニエルF

この答えは、JWTから派生したGoogleアクセストークンをデコードする私の問題を解決しました。他のすべての試みは「不正なパディング」という結果になりました。
John Hanley、

2

パディングを追加するのはむしろ...手間がかかります。これは、このスレッドのコメントと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)

2

base64.urlsafe_b64decode(data)Webイメージをデコードしようとしている場合は、簡単に使用できます。パディングは自動的に処理されます。


それは本当に役立ちます!

1

入力データが入力データを処理するPythonモジュールのbase64のb64decodeメソッドができるようにするために、より具体的かつOPに沿って、ここで説明する、または修正するには、2つの方法があり、何か非キャッチした例外を発生させずには:

  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(...)がデコードを終了した後、たとえばグループの4番目の=から、追加されたすべての文字が無視されるようです。

上記のいくつかのコメントで述べたように、[4を法とするそのポイントまでの解析済み文字数]値が0または3の場合、入力データの最後に必要なパディングは0、1、または2のいずれかです。または2、それぞれ。したがって、上記の項目3.と4.から、入力データに2つ以上の=を追加すると、そのような場合の[Incorrect padding]の問題が修正されます。

ただし、[解析された4を法とする文字の総数]が1の場合デコードは処理できません。デコードされた3バイトのグループの最初のデコードされたバイトを表すには、少なくとも2つのエンコードされた文字が必要です。で符号化入力データを破損し、この= 1の場合が起こることはない[N 4モジュロ]が、しかし文字が欠落することができると述べOPとして、それはここで起こり得ます。そのため、= sを追加するだけでは常に機能するわけではなく、A ==を追加しても機能しませんが、==を追加すると機能しません。注:[A]の使用は任意ですが、デコードされた(ゼロ)ビットのみをデコードに追加しますが、これは正しい場合も正しくない場合もありますが、オブジェクトは正確ではありませんが、base64.b64decode(...) 。

OPと特にその後のコメントから私たちが知っていること

  • Base64でエンコードされた入力データに欠落データ(文字)があると考えられます
  • Base64エンコードでは、標準の64のプレース値とパディングを使用します。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

このエラーがWebサーバーから発生した場合:投稿の値をURLエンコードしてみてください。「curl」を介してPOSTを実行していて、base64値をURLエンコードしていないため、「+」などの文字がエスケープされないため、Webサーバーの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, ' /')

デフォルトでは、altcharは'+/'です。


1
Python 3.7では機能しません。アサートlen(altchars)== 2、repr(altchars)
Dat TT

0

私もこの問題に遭遇し、何もうまくいきませんでした。ようやく、自分に合った解決策を見つけることができました。私はbase64でコンテンツを圧縮していましたが、これは100万のレコードのうち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)   

この回答によると、base64のように、理由はnullです。しかし、なぜエンコーダがこれを台無しにするのかはまだわかりません...

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.