読者です 読者をやめる 読者になる 読者になる

もろず blog

もろちゃんがITに関しての様々なトピックを解説します


OpenSSL の脆弱性 "Heartbleed" がヤバい?

セキュリティ OpenSSL Heartbleed


f:id:chanmoro999:20140411001024p:plain:h250

昨日、登録しているメーリングから
OpenSSL の脆弱性に関しての情報が何通も届きました

Webサーバーなどの脆弱性の情報が回ることは度々ありましたが、
何だか今回はいつもと様子が違うような感じがして、
これは相当なおおごとなんじゃ??と思って調べてみました


"OpenSSL 脆弱性" とかでググると、
パスワードが盗まれるとか、SSL証明書を更新しないとだめとか、
なんだかおどろおどろしい情報がたくさん出てきます

簡単に言うと今回の問題は
OpenSSL の "Heartbleed" という脆弱性をついた攻撃らしいです(?)


でも、これだけだと一体何がどれだけヤバいの?
というのがよくわからなかったので調べました


この記事では、
1.OpenSSL とは
2.Heartbeat Extension ってなんでしょう
3.Heartbleed を利用した攻撃
4.結局、何がヤバいの?
5.暗号技術の信頼性
について説明します


1.OpenSSL とは

SSL通信のプロトコルを実装したオープンソースの製品で、
とっても広く利用されています

特にWebサーバーで Apache + OpenSSL の組み合わせは相当多く使われているようです

インターネットでのSSL通信の半分くらいは、
OpenSSL を利用していると思ってもらっていいです


記事の先頭に戻る

2.Heartbeat Extension ってなんでしょう

今回の問題は OpenSSL の Heartbeat Extension という機能の脆弱性で、
ここから Heartbleed という名前がつけられたそうです


ですが、これだけ見て分かるわけないですよね
肝心の Heartbeat Extension とはどんな機能なんでしょうか?


検索しても "危険な脆弱性" というぺらっぺらな内容しか出てきません
Heartbeat Extension が悪いことのようなイメージを植え付けられるような気さえします


なので、RFCとソースを元にどんな機能なのかを追ってみました
雰囲気だけでも伝わればと思います


まず、"Heartbeat Extension" というプロトコルが RFC6520 に定義されており、
OpenSSL ではこのプロトコルを実装しています

※RFC6520の内容はコチラ
RFC 6520


Heartbeat という名前からして
何となくクラスタを組んだときの死活監視を思い浮かべていたのですが、
これはそのようなものではないようです


SSL 通信は実際には TLS、DTLS の通信を指します

Heartbeat Extension は
TLSでは keep-alive のような機能の実現として
DTLS では MTU のサイズをテストするための probeパケットとして
利用するようです


Heartbeat Extension の通信の中身は
Heartbeat のパケットが送られてきたらその内容をコピーしてそのまま返却する、
というとても単純な仕様です
※正確にはヘッダのtypeを"Heartbeatレスポンス"に書き換えます

ping の応答パケットみたいなものなのだと思ってもらえればいいです

f:id:chanmoro999:20140411011354j:plain
このパケットは
ハンドシェイクと呼ばれる通信を確立するフェーズで処理されます

なので特別な通信ではなく、
僕らのWebブラウザからも普通に送ることができます


これだけでは大問題になるなんて誰も想像もしないですよね
実際に、今回の問題はこのプロトコルが原因ではありません

ただ送られてきたデータをそのまま返すだけなんですから


でもまさか、そこに問題があったんです


記事の先頭に戻る

3.Heartbleed を利用した攻撃

さて、次に今回判明した Heartbleed 脆弱性を利用した攻撃の内容にうつりましょう

Heartbeat Extension は送られたパケットをコピーしてそのまま返す仕様でしたが、
OpenSSL はこの部分の実装に 意図しないメモリ領域のデータを返してしまう、
という致命的なバグがありました


OpenSSL の実装では 返却するパケットを生成する際に、
受け取ったパケットをメモリ上に保持し、
その先頭から"データ長"の分だけメモリのデータをコピーして返却しています

f:id:chanmoro999:20140411003542j:plain


実際に受け取ったパケットの長さとは別に、
送信元が設定した"データ長"というヘッダに値がセットされます

この"データ長"が実際に受け取ったパケットのデータ長と同じであれば
メモリに保持したパケットをそのまま指定することになるので何も問題ありません


ですがOpenSSL の実装では、この"データ長"と実際のパケットの長さを精査しておらず、
受け取った"偽データ長"もそのまま信じて利用していました

なので実際のデータの長さよりも大きな値を設定される、
という事態が発生します

f:id:chanmoro999:20140411013127j:plain


このデータ長には2バイト分の整数値を入れることができるので、
最大で 64KByte を指定することができます

データ長に限界の長さを設定して、データ内容を空にすることで
メモリの内容を 64KByte 分だけそのままコピーして送ることになります


ついでに問題のあったソースも見てみました

ssl/d1_both.c
dtls1_process_heartbeat() 内のソースを一部抜粋

// p: パケットのデータを保持したメモリのポインタ
unsigned char *p = &s->s3->rrec.data[0], *pl;
unsigned short hbtype;
unsigned int payload;
unsigned int padding = 16; /* Use minimum padding */
 
hbtype = *p++;
// ここでpayloadに"データ長"の値がセットされる
n2s(p, payload);
pl = p;

〜省略

// メモリ領域の確保
buffer = OPENSSL_malloc(1 + 2 + payload + padding);
bp = buffer;

〜省略

// typeに"Heartbeatレスポンス"を設定する
*bp++ = TLS1_HB_RESPONSE;
s2n(payload, bp);
// 応答パケットをメモリ上に作成
memcpy(bp, pl, payload);

※修正パッチの差分
git.openssl.org Git - openssl.git/commitdiff


この処理は、
相手が正当な証明書を持っているかどうかは全く関係なく動作します

誰なのかも分からない通信先からの通信に対して、
ご丁寧にメモリの内容を漏洩させる状態となっていた、ということです


しかも、プログラム的には何も異常な動作ではないため、
当然エラーログ等は吐かれません

なので、過去にこの攻撃を受けたかどうかは全くわからないんです


これが Heartbleed と呼ばれる脆弱性の中身です


対策には、最新のパッチを当てるか、
OPENSSL_NO_HEARTBEATS のフラグをセットしてコンパイルしなおすことで対応できるそうです

ソースの中をみると、
OPENSSL_NO_HEARTBEATS のフラグがあれば プリプロセッサで処理を省くようになってました

なので、やっぱり再コンパイルするしかありません


この脆弱性の原因になったバグ自体はとても単純なものです
ただの実装ミスとも言えるレベルです

こんな単純なものだからこそ注目されていなかったのかもしれません
このバグは2年前から存在していて、いままで放置されていたそうです


記事の先頭に戻る

4.結局、何がヤバいの?

メモリ上の情報が見れるということは、
SSL が解かれた後の情報も見ることができますし、
SSL の暗号化に利用される秘密鍵そのものが見られる場合もあります

そこにあるのは、
僕らがSNSにログインするときのパスワードかもしれません
ネットショッピングで使ったクレジットカードの情報かもしれません

インターネット上で送信した情報は全て入手される可能性がある、ということです
※ちょっと大げさに言ってますけど、あくまで可能性の話です


特に、秘密鍵が盗まれてしまうことは、
信頼性をぶっ壊す超非常事態になります

バグが埋め込まれたのは2年前ですが、
被害の対象は2年間ではないんです


記事の先頭に戻る

5.暗号技術の信頼性

暗号に利用する鍵が盗まれると、
全ての通信が丸見えになってしまうためもう意味がなくなってしまいます

しかも、SSL秘密鍵が盗まれた場合は、
そのSSL証明書の持ち主になりすますことができてしまいます
DNSキャッシュポイズニングなどと併せられると、
もうそれが本当のサイトかどうかは僕らには区別できません


SSL通信の鍵が盗まれた場合には 新規に鍵のペアを生成して、
SSL証明書を再発行をしなければいけません
そしてなるべく早く盗まれた鍵に対応するSSL証明書を失効させてください

このへんの感覚は家の鍵とかと全く同じです
なるべく早く家の鍵を付け替えるんです


それだけで済めばまだいいんですが、
もし、過去の通信が全て記録されていたとしたら
同じ鍵で暗号化された通信は全て復号できてしまいます

これができてしまうと、過去に成立していた信頼性が全て無になります
※この脅威に対応するための Perfect Forward Secrecy (PFS) というセキュリティ技術がありますが、まだまだ普及していません


つまり、盗まれた秘密鍵に対応するSSL証明書が発行された時から、
SSL証明書を破棄するまでの期間の情報が被害にあう可能性があります


誰がわざわざそんなことするの!って思うかもしれませんが、
ターゲットの情報を得ようと執拗に追いかけ回している人が世界中に1人もいない、
と言い切れるでしょうか?

個人や企業の情報を狙う "プロの悪い人" が1人もいない、
と言い切れるでしょうか?


しかもこのような "プロの悪い人" は
1つの攻撃から得た情報を元に何段階もの攻撃を仕掛けてきます

メールアドレス、パスワード、クレジットカード番号、住所、という情報から、
想像もつかないような被害を受ける可能性もあります

インターネットに接続することは、
世界中にいる "プロの悪い人" と常につながっていることと同じです


ちょっと話がズレますが、
公開鍵暗号の信頼性は、公開鍵に対応する秘密鍵を算出するのに、
とーーーーーーっても大変で時間がかかるという性質で担保されています

ここには、

  • しらみつぶしに計算していけばいつかは解が出る
  • 新しく生成した鍵のペアがすでに他の誰かが使っている場合がある

という、破られる可能性がほんのわずかに残っています


ただこれらは現在は現実的に不可能に近いと考えられているため、
まあ大丈夫でしょうという感じなんです
多項式時間で解けるということが証明されていないからです


ですが、高速に解を算出するアルゴリズムが発明されればその時点で全て破綻します

それがなかったとしても、
今後量子コンピューターなどの超高速で計算できる機械が登場すると、
成り立たなくなることがわかっています

また、あるビット数の鍵ペアの数は有限なので、
全ての鍵を計算で出すことも理論上は可能です



"セキュリティ" というと、ついしきいが高く感じてしまう領域なので、
「大丈夫!」とそれっぽい人が言ってくれれば信じてしまいそうになっちゃいます

今回の件にしてもそれぞれのシステムでは、
SSL通信にしてるから大丈夫!」 と思っちゃうのが普通ですよね


完璧ではないのに、完璧のていで進んでいるという、
なんだかとても変な感じがします

改めてセキュリティについて考えされられました


今回はそんなホットなニュースについてお届けしました


記事の先頭に戻る