Adobeのパスワード大量流出詳細を知って自分も考え直したこと


追記(2013/11/23):Twitterからのご指摘があり、ソースコードも含め大幅に改訂しました。

日本でも大きく報道されていましたが、先だってアドビが顧客パスワードを大量に流出したという事件がありました。

Adobeから盗まれたユーザー情報がネット上で公開される
http://itpro.nikkeibp.co.jp/article/IDG/20131031/515124/

Adobe – お客様情報のセキュリティに関する重要なお知らせ
http://blogs.adobe.com/japan-conversations/セキュリティに関する重要なお知らせ/

このニュースを読んだとき、僕は素朴に、「どうしてパスワードを生のまま保存しておくかなー」なんて
不思議に思ったものです。Adobeという大企業ともあれば、それなりの技術者はいるだろうし、
それなりの知識を持ち合わせているものだと思っていたからです。

ところが、以下のサイトで、詳細が説明されていて納得。

Anatomy of a password disaster – Adobe’s giant-sized cryptographic blunder
http://nakedsecurity.sophos.com/2013/11/04/anatomy-of-a-password-disaster-adobes-giant-sized-cryptographic-blunder/

要約すると、

  1. Adobeはパスワードを暗号化していると主張しているが、ハッシュ値に見えなくもない。
  2. なぜなら、その暗号化データのサイズはわずか8バイト(データサイズが小さいので解読される恐れがある)。
  3. Adobe独自の暗号アルゴリズムが使われていた可能性がある。
    もしそうなら、極めて危険で、考えられない仕様(※DES または3DESの可能性もあるが)。
  4. その暗号化データサイズと、同じデータ列のものが多く見られることから、IVもなく、ECBモードで暗号化されている。
  5. さらに、ユーザーがパスワードを忘れたときのために入力する「パスワードヒント」に、
    パスワード文字列をそのまま書き込む人が多くいた(笑)。
    ※しかもそのパスワードヒントはまったく暗号化されていなかった。
  6. その他にも「qwerty」「123456」とか、ありきたりで、よくある、簡単なパスワードを設定する人が多くいた。
  7. あとは大量にある同じデータ配列の「暗号化データ」から、
    クロスワードパズルを解くように大量のパスワードを導き出した(笑)。

 

「生パスワードをそのまま保存」という愚はおかしていなかったものの、
でもやっぱり残念な実装だった、というのは否めないようです。

 

saltつきのハッシュがベストプラクティス

Twitter上で、ユーザーパスワードを暗号化しておくよりも、
「ユーザーごとに固有のsaltを加えたハッシュ値を出すのがベストプラクティス」というご指摘をいただきました。

僕の方で、saltの解釈を誤解していたようで、ほぼCBC暗号でのIVの役割と同じことをするようです。

PHPでソースコードを書くと以下のようになります。


<?php

$user_id="m@hibara.org";
$password = "qwerty";

echo "UserID: ", $user_id, "<br />\n";
echo "Password: ", $password, "<br />\n";

//SHA-256の場合、salt文字列は16文字まで。
//それ以上が指定されると自動的にその長さに詰められます。
echo 'SHA-256:' . crypt($password, "$5$".$user_id);

?>

出力結果は、このような感じになります(毎回出力結果は変わります)。

UserID: m@hibara.org
Password: qwerty
SHA-256:$5$m@hibara.org$5ehqfKQFfy6lKz8kbDl2EB/aQ3JHyLCMA5W/rdT2LE.

なお、saltにメールアドレスを指定した場合、変更があったときに、
パスワードハッシュも変えなくてはならないので、そこだけは要注意。

PHP ver.5.5~が使えるなら、crypt()のラッパーで、互換性のある、
password_hash()を使うと、saltの自動生成を行ってくれるようです。


<?php

$password = "qwerty";
echo "Password: ", $password, "<br />\n";

echo "password_hash(): " . password_hash($password, PASSWORD_DEFAULT)."\n";

?>

出力結果は以下の通りです(毎回出力結果は変わります)。

Password: qwerty
password_hash(): $2y$10$Pw6tUB4HllZFM.oSZc5ZVu1y1xSUMw4E8ecfic/8utKdxlAheSOq2

Twitter上でのツッコミありがとうございました!

 

もしどうしてもパスワードを暗号化して格納するならCBCモードは必須

前述のサイトでも指摘されていましたが、少なくとも初期化ベクトル(IV)を利用した、
CBCモードで格納しておくのが解決策といえそうです。

改めてCBCモードとは何か? 
前にも記事
にしましたが、少しおさらいを。

毎回つくられる乱数(初期化ベクトル=IV)を与えることで、毎回暗号結果が異なるようになる仕組みのことでした。

今回問題だったのは、ECBモードです。

同じパスワードだと、同じ内容のデータ。そのまま変換されているだけですので、当然同じデータになります。

そこに、Adobeの件では、わずか8バイトのデータサイズで大量解析しやすかったこと、
また、他のユーザーが入力したヒントや、名前から、パスワードが推測され、ひもづけられたことで、
大量流出へとつながりました。

ですので、大本である、「同じパスワードを入力されても、ちがうデータになる」としておけば、
そこまでの被害にはならなかったはずです。

ためしに、PHPでどういった実装ができるか、調べてみました。

本家PHPサイトのドキュメントにあったソースコードのほぼ流用ですが、

http://us1.php.net/manual/ja/function.mcrypt-encrypt.php


<?php

//ユーザー登録(パスワードを暗号化する)
$password = "qwerty";
echo "password: ", $password, "<br />\n";

$email = "m@hibara.org";
echo "E-mail: ", $email, "<br />\n";

srand();
$iv_size = mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC);
$iv = mcrypt_create_iv($iv_size, MCRYPT_RAND);

$password_hash = sha1($password);
echo "Password SHA-1: ", $password_hash, "<br />\n";

//パスワードから、SHA-1ハッシュを取得して暗号化(暗号アルゴリズムはAES:Rijndael)
$ciphertext = mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $password, $password_hash, MCRYPT_MODE_CBC, $iv);

//出力するデータにIVをつけることを忘れずに
$ciphertext = $iv . $ciphertext;

//BASE64エンコード
$ciphertext_base64 = base64_encode($ciphertext);

echo "encrypted data: ", $ciphertext_base64 , "<br />\n";

//------------------------------------------------------------------------

//認証(復号する)
$ciphertext_dec = base64_decode($ciphertext_base64);

//IVサイズは128bit(16バイト)
$iv_dec = substr($ciphertext_dec, 0, $iv_size);

$ciphertext_dec = substr($ciphertext_dec, $iv_size);

$plaintext_dec = mcrypt_decrypt(MCRYPT_RIJNDAEL_128, $password, $ciphertext_dec, MCRYPT_MODE_CBC, $iv_dec);

echo "Password SHA-1: ", $plaintext_dec, "<br />\n";

?>

ただ、何万人もいるようなサイトで、これだけの処理をログイン時にやるには、
ちょっとサーバ負荷が高そうですね。ユーザー増加によるデータベースサイズも気になります。

やはり、saltつきハッシュで格納するのがベストのような気がします。

 

お気軽にコメントをどうぞ〜

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください

コメントフィード

s