入力長が3で割り切れない場合、base64エンコードでパディングが必要なのはなぜですか?


100

base64エンコーディングでパディングする目的は何ですか。以下はウィキペディアからの抜粋です。

「追加の埋め込み文字が割り当てられます。これを使用して、エンコードされた出力を4文字の整数倍に強制できます(または、エンコードされていないバイナリテキストが3バイトの倍数でない場合と同等)。これらの埋め込み文字は、デコード時に破棄する必要がありますが、入力バイナリ長が3バイトの倍数にならない場合でも、エンコードされていないテキストの有効な長さを計算できます(最後の非埋め込み文字は通常、エンコードされ、最後の6ビットブロックはゼロになります) -最下位ビットにパディングされ、エンコードされたストリームの最後に最大2つのパディング文字が発生する可能性があります。」

私は、任意の文字列をbase64エンコードし、base64エンコードされた文字列をデコードできるプログラムを作成しました。パディングはどのような問題を解決しますか?

回答:


208

パディングが不要であるというあなたの結論は正しいです。エンコードされたシーケンスの長さから、入力の長さを明確に決定することは常に可能です。

ただし、パディングは、たとえば非常に単純なネットワークプロトコルで発生する可能性があるように、個々のシーケンスの長さが失われるような方法でbase64エンコードされた文字列が連結される状況で役立ちます。

パディングされていない文字列が連結されている場合、個々のシーケンスの最後にある奇数バイトの数に関する情報が失われるため、元のデータを復元することはできません。ただし、パディングされたシーケンスが使用されている場合、あいまいさはなく、シーケンス全体を正しくデコードできます。

編集:イラスト

単語をbase64エンコードして連結し、ネットワーク経由で送信するプログラムがあるとします。「I」、「AM」、「TJM」をエンコードし、パディングせずに結果をサンドイッチして送信します。

  • IエンコードSQSQ==パディングあり)
  • AMエンコードQU0QU0=パディングあり)
  • TJMエンコードVEpNVEpNパディングあり)

したがって、送信されるデータはSQQU0VEpNです。レシーバーI\x04\x14\xd1Q)は、意図したの代わりにこれをbase64でデコードしIAMTJMます。送信者がエンコードされたシーケンスの各単語の終わりに関する情報破棄したため、結果はナンセンスです。送信者がSQ==QU0=VEpN代わりに送信した場合、受信者はこれを3つの別々のbase64シーケンスとしてデコードし、連結してを与えることができIAMTJMます。

なぜパディングを気にするのですか?

各単語の前に整数の長さを付けるようにプロトコルを設計しないのはなぜですか?その後、レシーバーはストリームを正しくデコードでき、パディングの必要はありません。

エンコードを開始する前に、エンコードするデータの長さがわかっている限り、これは素晴らしいアイデアです。しかし、言葉ではなく、ライブカメラからのビデオのチャンクをエンコードしていたとしたらどうでしょうか。各チャンクの長さが事前にわからない場合があります。

プロトコルがパディングを使用した場合、長さを送信する必要はまったくありません。データはカメラから入ってくるときにエンコードされ、各チャンクはパディングで終了し、レシーバーはストリームを正しくデコードできます。

明らかにこれは非常に不自然な例ですが、おそらくパディングが状況によっては役立つと考えられる理由を示しているのかもしれません。


22
+1「説明できない理由で冗長性と冗長性が好きだから」以外に、実際に合理的な答えを提供する唯一の答え。
2015

1
これは明確にエンコードされたチャンクでは問題なく動作しますが、デコード後に個別に連結されることが期待されます。U0FNSQ == QU0 =を送信すると、文を再構成できますが、文を構成する単語は失われます。何もないよりはましだと思います。特に、GNU base64プログラムは連結エンコーディングを自動的に処理します。
Marcelo Cantos 2016年

2
単語の長さが3の倍数である場合はどうなりますか?このばかげた連結方法は、パディングの除去ではなく、情報(単語の終わり)を破壊します。
GreenScape 16

2
Base64連結により、エンコーダーはチャンクサイズを3の倍数に揃える負担なしに、大きなチャンクを並列に処理できます。同様に、実装の詳細として、3の倍数ではないサイズの内部データバッファーをフラッシュする必要があるエンコーダーがそこにある場合があります。
Andre D

1
この答えは、「SQ == QU0 = VEpN」のようなものをデコーダに渡すだけでデコードできると考えるかもしれません。実際にはできないようです。たとえば、javascriptとphpの実装はこれをサポートしていません。連結された文字列から始めて、一度に4バイトをデコードするか、文字の埋め込み後に文字列を分割する必要があります。これらの実装は、文字列の途中であっても、埋め込み文字を無視するようです。
ローマ

38

関連するメモとして、ここに私があなたのために作成した任意のベース変換用のベースコンバータがあります。楽しい! https://convert.zamicol.com/

パディングキャラクターとは

パディング文字は長さの要件を満たすのに役立ち、意味を持ちません。

パディングの10進数の例: すべての文字列の長さが8文字であるという任意の要件がある場合、640は、「00000640」という意味を持たないため、先行する0をパディング文字として使用してこの要件を満たすことができます。

バイナリエンコーディング

バイトパラダイム:バイトは事実上の標準的な測定単位であり、エンコーディングスキームはバイトに関連付ける必要があります。

Base256はこのパラダイムに正確に適合します。1バイトは、base256の1文字に相当します。

16進数または16進数のBase16では、各文字に4ビットを使用します。1バイトは2つのbase16文字を表すことができます。

base256やbase16とは異なり、Base64はバイトパラダイムに均等に適合しません(base32にも適合しません)。すべてのbase64文字は、6ビットで表現でき、1バイトよりも2ビット短い。

base64エンコーディングとバイトパラダイムを分数として表現できます。1文字あたり6ビット、1バイトあたり8ビットです。この割合は、4文字で3バイトに削減されます。

この比率は、4つのbase64文字ごとに3バイトであり、base64をエンコードするときに従う規則です。 すべてのバイトがそれ自体で立つことができるbase16およびbase256とは異なり、Base64エンコードは3バイトバンドルでの測定でも約束することができます。

では、パディング文字がなくてもエンコーディングがうまく機能するにもかかわらず、パディングが推奨されるのはなぜですか?

ストリームの長さが不明な場合、またはデータストリームがいつ終了するかを正確に知ることが役立つ場合は、パディングを使用します。パディング文字は、それらの余分なスポットが空であり、あいまいさを排除する必要があることを明示的に伝えます。パディングで長さが不明な場合でも、データストリームの終了位置がわかります。

反例として、JOSEのような一部の標準ではパディング文字を許可していません。この場合、何かが欠けていると、暗号署名が機能しないか、他のbase64以外の文字( "。"など)が欠落します。長さについての仮定は行われませんが、何か問題がある場合は機能しないため、パディングは必要ありません。

そして、これはまさにbase64 RFCが言うことです、

状況によっては、ベースエンコードされたデータでパディング( "=")を使用する必要がないか、使用されません。一般的なケースでは、転送されたデータのサイズに関する仮定を行うことができない場合、正しいデコードされたデータを生成するためにパディングが必要です。

[...]

base 64のパディングステップ[...]が適切に実装されていない場合、エンコードされたデータの重要でない変更が発生します。たとえば、入力がbase 64エンコーディングの1オクテットのみの場合、最初のシンボルの6ビットすべてが使用されますが、次のシンボルの最初の2ビットのみが使用されます。これらのパッドビットは、以下のパディングに関する説明で説明されているように、準拠するエンコーダーによってゼロに設定する必要があります。このプロパティが保持されない場合、ベースエンコードされたデータの正規表現はなく、複数のベースエンコードされた文字列を同じバイナリデータにデコードできます。このプロパティ(およびこのドキュメントで説明されている他のプロパティ)が保持されている場合、正規のエンコーディングが保証されます。

パディングにより、失われたビットがないという約束でbase64エンコーディングをデコードできます。パディングなしでは、3バイトバンドルでの測定の明示的な確認応答はなくなります。パディングがないと、通常はTCP、チェックサム、またはその他の方法など、スタックの他の場所からの追加情報がないと、元のエンコーディングの正確な再生を保証できない場合があります。

RFC 4648の例を次に示します(http://tools.ietf.org/html/rfc4648#section-8

「BASE64」関数内の各文字は1バイト(base256)を使用します。次に、それをbase64に変換します。

BASE64("")       = ""           (No bytes used. 0%3=0.)
BASE64("f")      = "Zg=="       (One byte used. 1%3=1.)
BASE64("fo")     = "Zm8="       (Two bytes. 2%3=2.)
BASE64("foo")    = "Zm9v"       (Three bytes. 3%3=0.)
BASE64("foob")   = "Zm9vYg=="   (Four bytes. 4%3=1.)
BASE64("fooba")  = "Zm9vYmE="   (Five bytes. 5%3=2.)
BASE64("foobar") = "Zm9vYmFy"   (Six bytes. 6%3=0.)

これは、次のように操作できるエンコーダです。http//www.motobit.com/util/base64-decoder-encoder.asp


16
-1これは、数値システムがどのように機能するかについての詳細な記事ですが、エンコーディングがなくてもパディングが使用される理由を説明していません。
Matti Virkkunen 2014

2
あなたも質問を読みましたか?正しくデコードするためにパディングは必要ありません。
Navin

3
この回答は、実際にはここで述べられている理由を説明したと思います:「追加情報なしで元のエンコーディングの正確な再生を保証することはできません」。それは本当に簡単です、パディングは完全なエンコーディングを受け取ったことを知らせました。3バイトを使用するたびに、先に進んでデコードしても大丈夫であると安全に想定できます。心配する必要はありません。うーん...おそらく1バイト追加されてエンコードが変更される可能性があります。
Didier A.

@DidierA。base64部分文字列にさらに3バイトがないことをどのようにして知っていますか?をデコードするchar*には、文字列のサイズまたはnullターミネーターのいずれかが必要です。パディングは冗長です。したがって、OPの質問です。
Navin

4
@Navin base64バイトをストリームデコードする場合、長さがわからず、3バイトのパディングがあるため、3バイトを取得するたびに、ストリームの最後に到達するまで4文字を処理できることがわかります。これがないと、次のバイトで前の文字が変更される可能性があるため、バックトラックが必要になる場合があります。そのため、ストリームの最後に達したときにのみ適切にデコードできます。だから、それはあまり役に立ちませんが、あなたがそれを望むかもしれないいくつかのエッジケースがあります。
Didier A.

1

現代ではあまりメリットはありません。それでは、これを元の歴史的目的が何であったのかという質問として見てみましょう。

Base64エンコーディングは、1993 付けのRFC 1421で初めて登場しました。このRFCは実際には電子メールの暗号化に焦点を当てており、base64は4.3.2.4の1つの小さなセクションで説明されています。

このRFCでは、パディングの目的は説明されていません。元の目的について言及するのに最も近いのは次の文です。

完全なエンコーディングクォンタムは常にメッセージの最後に完了します。

連結(ここではトップの回答)も、パディングの明示的な目的としての実装の容易さも示唆されていません。ただし、説明全体を考慮すると、これがデコーダが32ビット単位("quanta")で入力を読み取るのを助けるために意図されていたと想定することは不合理ではありません。それは今日の利益にはなりませんが、1993年には、安全でないCコードが実際にこの特性を利用した可能性が非常に高いでしょう。


1
パディングがない場合、最初の文字列の長さが3の倍数ではないときに2つの文字列を連結しようとすると、多くの場合有効な文字列が生成されますが、2番目の文字列の内容は正しくデコードされません。パディングを追加すると、それが発生しないことが保証されます。
スーパーキャット2016

1
@supercatそれが目標である場合、すべてのbase64文字列を単一の "="で終了する方が簡単ではないでしょうか。平均の長さは短くなり、誤った連結が防止されます。
Roman Starkov

2
の平均の長さb'Zm9vYmFyZm9vYg==' b'Zm9vYmFyZm9vYmE=' b'Zm9vYmFyZm9vYmFy' b'Zm9vYmFyZm9vYmFyZg==' b'Zm9vYmFyZm9vYmFyZm8=' b'Zm9vYmFyZm9vYmFyZm9v' は、b'Zm9vYmFyZm9vYg=' b'Zm9vYmFyZm9vYmE=' b'Zm9vYmFyZm9vYmFy=' b'Zm9vYmFyZm9vYmFyZg=' b'Zm9vYmFyZm9vYmFyZm8=' b'Zm9vYmFyZm9vYmFyZm9v='
スコット
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.