POODLE攻撃は、運用泣かせというか、SSLv3のサポートを本当に切っちゃっていいのか、レガシークライアントについて対応するのがいいのか、悩む所ですよね。ShellShock騒ぎさえまだうちでは収束していないのに・・・

POODLE攻撃は、「SSLv3の問題であってTLSv1.0以上では(パディングの方法が違うので)影響が無い」とされていますが、nahiさんからコメントもらって「実装依存だけどTLSv1.0でも危険な実装があるんじゃないの?」ということで、その事を書いてみたいと思います。

今回のPOODLE攻撃については、何が問題でどのようにすれば攻撃できるのかという、詳しい図入りの素晴らしく判りやすい解説をモバゲーさんが 「SSL v3.0の脆弱性「POODLE」ってかわいい名前だけど何?? - Padding Oracle On Downgraded Legacy Encryptionの仕組み -」でされているので、そちらをご覧になると良いと思います。

SSLv3とTLSv1.0以降のブロック暗号のパディングの違い

SSL/TLSでAESや3DESなどのブロック暗号を使った場合には、暗号化するデータはブロックの大きさの整数倍、つまり、AES-256なら32バイト、AES-128なら16バイト、3DESなら24バイトの倍数でないといけません。で、その大きさに揃うようにパディングと呼ばれる隙間の詰め物をして倍数になるように調整するわけです。

例えば、暗号スイートとして、AES-128(16バイト)、SHA1(20バイト)を使っているとして、8バイトのデータを暗号化するとしましょう。パディングした暗号化するための入力は

平文(8)+HmacSHA1メッセージ認証コード(20)+パディング(3)+パディング長(1)=AESブロック長(16)×2
となります。必ずパディング長の1バイトは含まれます。パディングの長さは(ブロック長-1)を超えない値で今回は3となります。

さて、SSLv3とTLSv1.0のパディング方法の違いですが、

  • SSLv3の場合はパディングの値は任意の値を取れる
  • TLSv1.0の場合はパディングはPKCS#5パディング方式を用い、 具体的にはパディング値の各バイトはパディング長と同じ値が設定される。
となっています。先ほどの3バイトのパディングがあるケースでは 下の例のようになります。
パディング(3)+パディング長 [a0][f3][62][03] - SSLv3の場合(先頭3バイトは任意) [03][03][03][03] - TLSv1.0の場合(先頭3バイトはバイト長と同じ値)
今回のPOODLE攻撃では、SSLv3がパディングの値が任意であるために、 効率的に特定の位置の1バイトの平文を復元することができるわけです。 モバゲーさんの解説にある通り、SSLv3の場合は256回のHTTPS要求の試行で 1バイトを解読することができますが、TLSv1.0の場合には256の16乗の 試行が必要なために現実的な時間で復元することはできません。

で、本当にTLSv1.0なら安全なの?

今日の本題のTLSv1.0なら本当に安全なのか、という話なんですが、 とりあえず、TLSv1.1とTLSv1.0のパディングに関する規定を 見てみましょう。

まず、TLSv1.1についてですが、

RFC 4346 TLSv1.1 6.2.3.2 CBC Block Cipherより
padding
Padding that is added to force the length of the plaintext to be an integral multiple of the block cipher's block length. The padding MAY be any length up to 255 bytes, as long as it results in the TLSCiphertext.length being an integral multiple of the block length. Lengths longer than necessary might be desirable to frustrate attacks on a protocol that are based on analysis of the lengths of exchanged messages. Each uint8 in the padding data vector MUST be filled with the padding length value. The receiver MUST check this padding and SHOULD use the bad_record_mac alert to indicate padding errors.
となっています。

一方、TLSv1.0です。

RFC 2246 TLSv1.0 6.2.3.2 CBC Block Cipher より
padding
Padding that is added to force the length of the plaintext to be an integral multiple of the block cipher's block length. The padding may be any length up to 255 bytes long, as long as it results in the TLSCiphertext.length being an integral multiple of the block length. Lengths longer than necessary might be desirable to frustrate attacks on a protocol based on analysis of the lengths of exchanged messages. Each uint8 in the padding data vector must be filled with the padding length value.

TLSv1.0では、「パディングデータはパディング長で埋められなければならない。」 と書いてありますが、 TLSv1.1では、「 パディングデータはパディング長で埋められなければならない(MUST)。 受信者はこのパディングをチェックしなければならず(MUST)、 パディングエラーを示すにはbad_record_macアラートを使う べきである(SHOULD)。」 となっています。 つまり、TLSv1.0ではパディングデータがちゃんとパディング長の値で うめられているかどうかのチェックはmustとは書いてあり、 多くの実装はちゃんとしてくれていると思いますが、 RFC上の必須要件(MUST)ではないので、チェックしない実装があっても おかしくなく、 チェックされない場合、これではSSLv3と同じで、一般的なSSL/TLSの実装では MUSTと書かれていない事は実装する必要もないので (実際、SHOULDと書いてあれば、これに対応しない実装も多い)、 実装によってはパディング値のチェックをしないために、 SSLv3と同じくPOODLE攻撃の影響を受けるTLSv1.0実装がある可能性がある かもしれないという事です。

時間がある時に、ちょっと主要なオープンソースの実装を覗いてみようと思います。

今日はこの辺で。nahiさん、情報ありがとうございました。

追記