web-dev-qa-db-ja.com

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

パディングエラーがあったとしても、バイナリに変換したいbase64エンコードのデータがあります。私が使用する場合

base64.decodestring(b64_string)

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

更新:すべてのフィードバックをありがとう。正直に言うと、言及されているすべての方法は少し失敗したように思えたので、opensslを試してみることにしました。次のコマンドは、うまく機能しました。

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

他の応答で述べたように、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

74
Simon Sapin

必要に応じてパディングを追加してください。ただし、マイケルの警告に注意してください。

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

パディングエラーがある場合は、おそらく文字列が破損していることを意味します。 base64でエンコードされた文字列の長さは4の倍数でなければなりません。文字列を4の倍数にするために、パディング文字(=)を自分で追加することもできますが、何か問題がなければ、すでにそれを持っているはずです

23
Michael Mrozek

「不適切なパディング」とは、「パディングがない」だけでなく、「信じられないかもしれませんが」「不正なパディング」も意味します。

「パディングの追加」メソッドが機能しない場合は、後続のバイトをいくつか削除してください。

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]にないものです。次に、base64.b64decode()の2番目の引数で使用する必要がある順序を確認するために実験する必要があります。

更新:データが「企業機密」の場合:
(a)前もって言う必要があります
(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
21
John Machin

つかいます

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'

>>> 
17
warvariuc

コメントする担当者はいませんが、注意すべき素晴らしい点は、(少なくともPython 3.xで)base64.b64decodeは、そもそも十分な余白があれば、余分なパディングを切り捨てることです。

そのため、b'abc='b'abc=='と同様に機能します。

つまり、必要なパディング文字の最大数(3つ(b'==='))を追加するだけで、base64は不要な文字を切り捨てます。

基本的に:

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

よりきれいです

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

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

URLセーフアルファベットを使用して文字列sをデコードします。これは、標準のBase64アルファベットの+の代わりに-および/の代わりに_を使用します。

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

4
Daniel F

ここで説明されている入力データを修正する、またはより具体的にはOPに沿って、Pythonモジュールbase64のb64decodeメソッドが入力データをに処理できるようにする2つの方法があります未捕捉の例外を発生させることなく何か

  1. 入力データの最後に==を追加し、base64.b64decode(...)を呼び出します
  2. 例外が発生した場合、

    私。 try/exceptでキャッチし、

    ii。 (R?)入力データから任意の=文字を取り除きます(N.B.これは必要ないかもしれません)、

    iii。入力データにA ==を追加します(A ==からP ==が機能します)。

    iv。これらのA ==-appended入力データでbase64.b64decode(...)を呼び出します

上記の項目1または項目2の結果は、望ましい結果をもたらします。

注意事項

これは、デコードされた結果が最初にエンコードされたものであることを保証するものではありませんが、(時には?)OPが動作するのに十分です:

破損したとしても、ASN.1ストリームからいくつかの有用な情報を取得できるので、バイナリに戻したいと思います」。

以下の知識および仮定を参照してください。

TL; DR

Base64.b64decode(...)のいくつかの簡単なテストから

  1. 非[A-Za-z0-9 + /]文字を無視するようです。 = sを無視することを含む(= --- ==)unlessそれらは4つの解析グループの最後の文字であり、その場合= sはデコードを終了します(a = b = c = d =はabc =と同じ結果を与え、a == b == c ==はab ==と同じ結果を与えます。

  2. また、all文字が追加されたbase64の後にポイントが無視される .b64decode(...)はデコードを終了しますグループの4番目として=から。

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

HOWEVER、デコードは、[4を法とする解析された文字の総数]が1の場合を処理できません。 3つのデコードされたバイトのグループ内の最初のデコードされたバイト。 un破損したエンコードされた入力データでは、この[N modulo 4] = 1のケースは決して起こりませんが、OPは文字が欠落しているかもしれないと述べたので、ここで起こる。単純に= sを追加しても常に機能するとは限らず、A==を追加しても機能しないのはそのためです。 N.B. [A]の使用はすべてall意的です:デコードされた(ゼロ)ビットのみをデコードに追加します。これは正しい場合も正しくない場合もありますが、ここのオブジェクトは正確ではなくbase64.b64decode(...)sans例外による完了です。

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

  • Base64でエンコードされた入力データに欠落データ(文字)があると思われます
  • Base64エンコードでは、標準の64のプレース値とパディングが使用されます。 a-z; 0-9; +; /; =はパディングです。これは、openssl enc ...が機能するという事実によって確認または少なくとも示唆されています。

仮定

  • 入力データには7ビットのASCIIデータのみが含まれます
  • 唯一の破損は、エンコードされた入力データの欠落です
  • OPは、エンコードされた入力データの欠落に対応する後のどの時点でも、デコードされた出力データを気にしません

Github

このソリューションを実装するラッパーは次のとおりです。

https://github.com/drbitboy/missing_b64

1
Brian Carcich

パディングの追加はかなり...面倒です。以下は、このスレッドのコメントと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)
1
Bryan Lott

ターゲット文字列値をデコードする前に、「=」などの文字を追加して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
Curtis Yallop

私の場合、メールの解析中にそのエラーに直面しました。添付ファイルをbase64文字列として取得し、re.searchで抽出しました。最終的に、奇妙な追加の部分文字列が最後にありました。

dHJhaWxlcgo8PCAvU2l6ZSAxNSAvUm9vdCAxIDAgUiAvSW5mbyAyIDAgUgovSUQgWyhcMDAyXDMz
MHtPcFwyNTZbezU/VzheXDM0MXFcMzExKShcMDAyXDMzMHtPcFwyNTZbezU/VzheXDM0MXFcMzEx
KV0KPj4Kc3RhcnR4cmVmCjY3MDEKJSVFT0YK

--_=ic0008m4wtZ4TqBFd+sXC8--

--_=ic0008m4wtZ4TqBFd+sXC8--を削除して文字列を削除すると、解析が修正されました。

したがって、正しいbase64文字列をデコードしていることを確認してください。

0
Daniil Mashkin

あなたが使用する必要があります

base64.b64decode(b64_string, ' /')

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

0
Quoc