Slide 1

Slide 1 text

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

Slide 2

Slide 2 text

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

Slide 3

Slide 3 text

背景 ● パスワードをDBに保存するときに、具体的に どう保存すればいいのか – ハッシュ化 + ソルト – 使用するアルゴリズムはどれにすべきか? – ちゃんと整理したい ● 昨日の深夜にサクッと作ったので気軽にマサカ リ投げてください ● Djangoでどういった実装がされているのか

Slide 4

Slide 4 text

Case 1 ● パスワードを平文のまま保存 +------+--------+------------+ | id | name | password | |------+--------+------------| | 1 | admin | password | +------+--------+------------+

Slide 5

Slide 5 text

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

Slide 6

Slide 6 text

Case2 ● DB内でパスワードを暗号化 +------+--------+------------------+ | id | name | password | |------+--------+------------------| | 1 | admin | 402fda0cde783273 | +------+--------+------------------+

Slide 7

Slide 7 text

暗号化して保存 ● Ad be ○ が過去に採用していたケース ● 暗号化されて安全だね!やったー! – とはならない ● 暗号化するプログラムが入手されたら? ● 同じパスワードからは同じ文字列が生成される

Slide 8

Slide 8 text

Case3 ● パスワードをハッシュ化して保存 +------+--------+----------------------------------+ | id | name | password | |------+--------+----------------------------------| | 1 | admin | 5f4dcc3b5aa765d61d8327deb882cf99 | +------+--------+----------------------------------+

Slide 9

Slide 9 text

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

Slide 10

Slide 10 text

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

Slide 11

Slide 11 text

余談 ● MD5 Collision – http://www.mscs.dal.ca/~selinger/md5collisio n/ – 異なるファイルでもMD5ハッシュ値が同じ ● SHA1でもMD5ほどではないが、衝突の危険性 がある

Slide 12

Slide 12 text

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

Slide 13

Slide 13 text

Case4 ● ハッシュ + ソルトを使用 +------+--------+--------------------------------+ | id | name | password | |------+--------+--------------------------------| | 1 | admin | $1$salt$qJH7.N4xYta3aEG/dfqo/0 | +------+--------+--------------------------------+

Slide 14

Slide 14 text

ハッシュ+ソルト ● 16byte以上のソルトを使用すること ● ランダムなソルトを使用 – random()は使わない – CryptAPIとか/dev/urandomとか ● これで同じパスワードから同じハッシュ値が生 成される問題はなくなった ● だいぶ堅牢になったぞ!!!!!

Slide 15

Slide 15 text

ストレッチング ● ハッシュ + ソルトな方式を使用することで かなり堅牢になった ● しかし、まだだ!!! ● 最新GPUを大量に積めば一瞬で大量のSHA- 256 Hashを計算することも可能 – 最近ではAWSに金をつぎ込むことでも可能! ● 現実的に計算できないようにするためにスト レッチングを行う

Slide 16

Slide 16 text

ストレッチング ● ハッシュの計算を何回も繰り返す ● PBKDF2, bcryptなどの使用が推奨 – 独自で実装をしてはいけない – 確かNISTが資料を出していたはず(要出典)

Slide 17

Slide 17 text

PBKDF2 ● KDF(Key Derivation Function) – 鍵導出関数 – 固定長で一定のエントロピーを持つより安全な鍵を 得ることが可能 – パラメータに繰り返し回数があり、計算量を調整で きる ● PBKDF2はRFC2898に基づたKDF

Slide 18

Slide 18 text

PBKDF2 ● DK = PBKDF2(PRF, Password, Salt, c, dkLen) – PRF : 疑似乱数生成関数. HMAC – Password : パスワード – Salt : ソルト – C : 繰り返す回数 – DkLen : 出力する導出鍵の長さ

Slide 19

Slide 19 text

HMAC ● MAC(Message Authentication Code)の1つ – 共通鍵を用いて固定長のメッセージを出力する – 改竄検知に使用

Slide 20

Slide 20 text

MD(Message Digest) ● 所謂、一方向ハッシュ関数 – 任意のメッセージから固定長メッセージを生成 – admin → 5f4dcc3b5aa765d61d8327deb882cf99 ● 異なるメッセージから同一のハッシュ値を生成 することが困難でなければいけない ● ちなみにCRC32のような誤り検出符号は暗号 的ハッシュ関数とは区別される

Slide 21

Slide 21 text

MDとMAC ● MD ... メッセージを容易に計算できる ● MAC … 共通鍵がなければ計算できない ● 共通鍵なので否認を防止することができない ● つまり、メッセージを生成したものが本人であ るという保証はされない

Slide 22

Slide 22 text

HMAC ● ハッシュ関数 + MAC ● すべてのハッシュ関数に適用可能 – それぞれHMAC-MD5とかHMAC-SHA1とか ● 他にもCBC-MACなどもある – ブロック暗号アルゴリズムと組み合わせる方式 – OMAC/CMAC, PMAC

Slide 23

Slide 23 text

PBKDF2 ● DK = PBKDF2(PRF, Password, Salt, c, dkLen) – PRF : 疑似乱数生成関数. HMAC – Password : パスワード – Salt : ソルト – C : 繰り返す回数 – DkLen : 出力する導出鍵の長さ ● で、何回繰り返せばいい???

Slide 24

Slide 24 text

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='

Slide 25

Slide 25 text

Djangoでの実装 user.password.split('$') ['pbkdf2_sha256', '24000', 'NPCTHzvjHgPo', # salt 'phHqNmNOafrNsxYXZm2HNpPBXzqBVijvakAFVkRHbZs='] ● PBKDF2 + HMAC + SHA256で24000回繰り返している

Slide 26

Slide 26 text

ちなみに ● 最新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

Slide 27

Slide 27 text

計算速度の問題 ● 結構な回数を計算していることがわかったが... ● 非常に長いパスワードを計算すると、かなり時 間がかかるのでは?

Slide 28

Slide 28 text

計測してみた ● 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)

Slide 29

Slide 29 text

計測してみた ● 64文字のとき…11.665050101000816 ● 10MBのとき… 32.772370806000254 ● という感じなので、Djangoでは4098文字制限 がある – https://www.djangoproject.com/weblog/2013/ sep/15/security/

Slide 30

Slide 30 text

まとめ ● パスワードを保存する際には以下の要件を必ず満たす 必要がある – 管理者でもユーザーのパスワードを復元できない – 同一パスワードから同一ハッシュ値を生成しない ● 具体的には – 疑似乱数発生関数である程度の長さのソルトを使ってハッ シュ値を生成する – KDFで十分な回数繰り返す ● WAFでは標準で実装されていたり、ライブラリも充実 しているので積極的に頼っていくべき

Slide 31

Slide 31 text

感想と疑問 ● マシンの性能を考えてストレッチングの回数を 増減したほうが良さそう ● マシンやGPUの性能向上に伴って使用するアル ゴリズムやストレッチングの回数を変更してい なければならない ● 変更するときにどうすればいいのか – ログイン時に再生成? – 緊急のときはアカウント停止?