Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Webアプリケーションにおけるパスワードの管理について

B49933741d74e122bc1314b2975e9fc9?s=47 mrtc0
September 12, 2016

 Webアプリケーションにおけるパスワードの管理について

#LT駆動 29での発表スライド

B49933741d74e122bc1314b2975e9fc9?s=128

mrtc0

September 12, 2016
Tweet

Transcript

  1. Webアプリケーションにおける パスワードの管理について 2016-09–04 LT駆動開発 #29 @mrtc0

  2. > whoami • もりたこ(@mrtc0) • ChainerでXSS検知するやつ作った – https://mrtc0.com/lab/php/index.php?q=hello

  3. 背景 • パスワードをDBに保存するときに、具体的に どう保存すればいいのか – ハッシュ化 + ソルト – 使用するアルゴリズムはどれにすべきか?

    – ちゃんと整理したい • 昨日の深夜にサクッと作ったので気軽にマサカ リ投げてください • Djangoでどういった実装がされているのか
  4. Case 1 • パスワードを平文のまま保存 +------+--------+------------+ | id | name |

    password | |------+--------+------------| | 1 | admin | password | +------+--------+------------+
  5. 平文での保存 • 推奨されない(当たり前) • 第三者が容易にログインできる • 他人のパスワードの生成パターンを予測できる – Aさんは 文字列+誕生日

    とか
  6. Case2 • DB内でパスワードを暗号化 +------+--------+------------------+ | id | name | password

    | |------+--------+------------------| | 1 | admin | 402fda0cde783273 | +------+--------+------------------+
  7. 暗号化して保存 • Ad be ◦ が過去に採用していたケース • 暗号化されて安全だね!やったー! – とはならない

    • 暗号化するプログラムが入手されたら? • 同じパスワードからは同じ文字列が生成される
  8. Case3 • パスワードをハッシュ化して保存 +------+--------+----------------------------------+ | id | name | password

    | |------+--------+----------------------------------| | 1 | admin | 5f4dcc3b5aa765d61d8327deb882cf99 | +------+--------+----------------------------------+
  9. ハッシュ化して保存 • 一方向ハッシュ関数を使用して保存 – パスワードの長さに関する情報はなくなった – ユーザーにパスワード長を制限する必要もない – 可逆性がなくなった •

    しかし...!
  10. ハッシュ化して保存 • 単にハッシュ化して保存するだけではダメ – 同じパスワードからは同じハッシュ値が生成される • レインボーテーブルを使用したクラック – 事前にパスワード:ハッシュ値のデータを容易して おく(レインボーテーブル)

  11. 余談 • MD5 Collision – http://www.mscs.dal.ca/~selinger/md5collisio n/ – 異なるファイルでもMD5ハッシュ値が同じ •

    SHA1でもMD5ほどではないが、衝突の危険性 がある
  12. パスワード保存の要件 • DBパスワードを保存する際には以下の要件を 必ず満たす必要がある – 管理者でもユーザーのパスワードを復元できない – 同一パスワードから同一ハッシュ値を生成しない – 十分にストレッチングを行う

  13. Case4 • ハッシュ + ソルトを使用 +------+--------+--------------------------------+ | id | name

    | password | |------+--------+--------------------------------| | 1 | admin | $1$salt$qJH7.N4xYta3aEG/dfqo/0 | +------+--------+--------------------------------+
  14. ハッシュ+ソルト • 16byte以上のソルトを使用すること • ランダムなソルトを使用 – random()は使わない – CryptAPIとか/dev/urandomとか •

    これで同じパスワードから同じハッシュ値が生 成される問題はなくなった • だいぶ堅牢になったぞ!!!!!
  15. ストレッチング • ハッシュ + ソルトな方式を使用することで かなり堅牢になった • しかし、まだだ!!! • 最新GPUを大量に積めば一瞬で大量のSHA-

    256 Hashを計算することも可能 – 最近ではAWSに金をつぎ込むことでも可能! • 現実的に計算できないようにするためにスト レッチングを行う
  16. ストレッチング • ハッシュの計算を何回も繰り返す • PBKDF2, bcryptなどの使用が推奨 – 独自で実装をしてはいけない – 確かNISTが資料を出していたはず(要出典)

  17. PBKDF2 • KDF(Key Derivation Function) – 鍵導出関数 – 固定長で一定のエントロピーを持つより安全な鍵を 得ることが可能

    – パラメータに繰り返し回数があり、計算量を調整で きる • PBKDF2はRFC2898に基づたKDF
  18. PBKDF2 • DK = PBKDF2(PRF, Password, Salt, c, dkLen) –

    PRF : 疑似乱数生成関数. HMAC – Password : パスワード – Salt : ソルト – C : 繰り返す回数 – DkLen : 出力する導出鍵の長さ
  19. HMAC • MAC(Message Authentication Code)の1つ – 共通鍵を用いて固定長のメッセージを出力する – 改竄検知に使用

  20. MD(Message Digest) • 所謂、一方向ハッシュ関数 – 任意のメッセージから固定長メッセージを生成 – admin → 5f4dcc3b5aa765d61d8327deb882cf99

    • 異なるメッセージから同一のハッシュ値を生成 することが困難でなければいけない • ちなみにCRC32のような誤り検出符号は暗号 的ハッシュ関数とは区別される
  21. MDとMAC • MD ... メッセージを容易に計算できる • MAC … 共通鍵がなければ計算できない •

    共通鍵なので否認を防止することができない • つまり、メッセージを生成したものが本人であ るという保証はされない
  22. HMAC • ハッシュ関数 + MAC • すべてのハッシュ関数に適用可能 – それぞれHMAC-MD5とかHMAC-SHA1とか •

    他にもCBC-MACなどもある – ブロック暗号アルゴリズムと組み合わせる方式 – OMAC/CMAC, PMAC
  23. PBKDF2 • DK = PBKDF2(PRF, Password, Salt, c, dkLen) –

    PRF : 疑似乱数生成関数. HMAC – Password : パスワード – Salt : ソルト – C : 繰り返す回数 – DkLen : 出力する導出鍵の長さ • で、何回繰り返せばいい???
  24. Djangoの実装を見てみる • Djangoでは標準で、ユーザーのパスワードを PBKDF2で保存している from django.contrib.auth.models import User user =

    User(username="user") user.set_password('password') user.password 'pbkdf2_sha256$24000$NPCTHzvjHgPo$phHqNmNOafrNsxYXZm2HNpPBXzqBVijvakAFVkR HbZs='
  25. Djangoでの実装 user.password.split('$') ['pbkdf2_sha256', '24000', 'NPCTHzvjHgPo', # salt 'phHqNmNOafrNsxYXZm2HNpPBXzqBVijvakAFVkRHbZs='] • PBKDF2

    + HMAC + SHA256で24000回繰り返している
  26. ちなみに • 最新Ver(1.10)では24000回だったが... • マシンの性能の向上に伴って計算回数も増えて いっている Ver 回数 1.9 2015.12

    24000 1.8 2015.01 20000 1.7 2014.02 15000 1.6 2013.06 12000 1.5 2013.02 10000
  27. 計算速度の問題 • 結構な回数を計算していることがわかったが... • 非常に長いパスワードを計算すると、かなり時 間がかかるのでは?

  28. 計測してみた • i5-2520M 2.50GHz – 64文字と10000000文字(10MB)で試す from timeit import timeit

    setup = ''' from django.contrib.auth.hashers import PBKDF2PasswordHasher hasher = PBKDF2PasswordHasher() password = "A" * 10000000 salt = "salt" ''' result = timeit(stmt = 'hasher.encode(password = password, salt = salt, iterations = 24000)', setup=setup, number=500) print(result)
  29. 計測してみた • 64文字のとき…11.665050101000816 • 10MBのとき… 32.772370806000254 • という感じなので、Djangoでは4098文字制限 がある –

    https://www.djangoproject.com/weblog/2013/ sep/15/security/
  30. まとめ • パスワードを保存する際には以下の要件を必ず満たす 必要がある – 管理者でもユーザーのパスワードを復元できない – 同一パスワードから同一ハッシュ値を生成しない • 具体的には

    – 疑似乱数発生関数である程度の長さのソルトを使ってハッ シュ値を生成する – KDFで十分な回数繰り返す • WAFでは標準で実装されていたり、ライブラリも充実 しているので積極的に頼っていくべき
  31. 感想と疑問 • マシンの性能を考えてストレッチングの回数を 増減したほうが良さそう • マシンやGPUの性能向上に伴って使用するアル ゴリズムやストレッチングの回数を変更してい なければならない • 変更するときにどうすればいいのか

    – ログイン時に再生成? – 緊急のときはアカウント停止?