パスワード認証は容易に実装出来るためもっとも広く使われているユーザー認証方法です.

パスワード認証

パスワード認証は,

  • ユーザー識別子
  • パスワード

の 2 つの値によってユーザーを認証します.

ユーザーの識別子には ユーザーIDメールアドレス が使われます. 通常は安全性を考えて識別子にはニックネームなどの一般公開しているものでなく, メールアドレスなどの 非公開のもの を採用します(Twitter は利便性を重視して一般公開されているユーザー名とメールアドレスの両方を識別子として利用できます).

パスワード認証に対する攻撃

パスワード認証に対する攻撃には以下のものがあります:

総当たり攻撃 (Brute force attack)
全てのパスワードをでだらめに試行すること.
類推攻撃
利用者に関する情報から類推されるパスワードを試行すること.
辞書攻撃 (Dictionary attack)
パスワードとして使用されやすいと考えられるリストを作成しそれらを試行すること.
事前計算攻撃
事前に得たパスワードのハッシュ値などを解析してパスワードを導き出して不正ログインを行う.

上の定義では事前計算攻撃は攻撃を行った時点で攻撃が成功しています.
それ以外の攻撃は不正ログインが行われなくとも, パスワードを「試行すること」を攻撃として定義しています. 実際にパスワードを試行する総当たり攻撃, 類推攻撃, 辞書攻撃を パスワード試行攻撃 と呼ぶことにします. 一般的にはより広義な意味でパスワードを探り当てることを パスワードクラック と呼びます.

例えば適当なサイトのログイン画面に行き, 適当なIDとパスワードを入力してログインボタンを押したとします. これは(試行回数は一回ですが)総当たり攻撃です.
知人のアカウントで知人が使っていそうなパスワードを考えてログインしようとすれば, これは類推攻撃です.
一般的に使われていそうなパスワードとして password を試したとすると, これは辞書攻撃です.
これら 3 つの攻撃は失敗することもあるし, 成功することもありますが, 事前計算攻撃には「失敗」も「成功」もありません. ユーザー認証システムにおいて,

\[
事前計算攻撃される \Rightarrow 不正ログイン発生
\]

です. 事前計算を「ハッシュ値からハッシュ化前のパスワードの値を導き出すために行う計算」と定義すれば, 事前計算の成功により事前計算攻撃が可能となります. 一般的にこの意味での事前計算は オフライン攻撃 と呼ばれます.

ハッシュ化せずにデータベースに保存していたパスワードが SQL インジェクションなどにより漏洩し, 不正ログインが発生するということも考えられます.

いずれの攻撃においても対策は パスワードの長さを長くする こと, 意味がなくランダム性の高い文字列にすることです. また ハッシュ化にユーザーごとに異なるソルトを使う ことで事前計算攻撃に対する耐性は歴然と大きくなります.

パスワードの漏洩

パスワードは以下のような方法でも攻撃者の手に渡ることがあります:

  • アプリケーションの脆弱性をつかれることによる漏洩.
  • 偽サイトや偽ソフトでパスワードを入力して攻撃者に送信してしまう.
  • スパイウェアによってキーボードで入力したパスワードが攻撃者に送信される.
  • スパイウェアによってパスワードをメモしたメモ帳などのデータが攻撃者に送信される.
  • その他物理的な方法(手元を盗み見される, メモした紙を見られる, 白状させられる)

偽サイトを用意してプライバシー情報を入手することを フィッシング, パスワードなどを入力しているときに画面や手元を盗み見することでプライバシー情報を入手することを ショルダーハッキング と呼びます. フィッシングやショルダーハッキングなどのように利用者をだます手法は ソーシャルハッキング と言います.

パスワードを盗まれないようにするためには

ユーザーが行うこと

一般的な攻撃(総当り/類推/辞書/事前計算)に対しては, とにかく 長くてランダム性の高い文字列 にすることです.
加えて, 信頼できないサイトではパスワードを入力しない, スパイウェアなどに感染しないようにする, 出来るだけメモ帳や紙などにメモせず記憶しておく, 見られないようにこっそり入力する, 白状しないということが重要です.

サーバーが行うこと

第一に SQL インジェクション, CSRF の脆弱性を生じさせないことです.
パスワードは生の値ではなく, 必ずハッシュ化した値を保存します. オフライン攻撃対策のためハッシュ化には必ずユーザーごとに異なるソルトを使います.
内外の人間によるデータの盗難が発生しないような体制を整えます.

もっとも典型的なパスワードポリシーは以下のようなものです:

  • パスワードの 最小文字長 を設定する.
  • パスワードの 使用文字制限 を設ける.
    例えば数字とアルファベットが混合した文字列のみに制限する, 推測されやすい単語(password, passwd, pass など)を禁止する. ユーザー名やユーザーID, メルアドと同じパスワードを禁止するなど.
    使用文字と文字長などからパスワードの強度を見積もってユーザーにその強度を示す場合があります. 例えば Twitter ではパスワード設定用の入力欄にパスワードを入力するとリアルタイムでパスワードの強度が「弱い」「良い」「強い」「安全性はかなり高いです」の 4 段階で表示されます.
  • パスワード入力欄を マスク表示 にする.
    ショルダーハッキングの防止になりますがパスワードが見えないのでユーザービリティが悪くなります. またユーザーが複雑なパスワードを避ける原因になります. 最近ではユーザビリティを優先してパスワードの文字を表示するためのチェックボックスを設置しているところが増えいています.
  • ログイン失敗時は 原因が特定できないエラーメッセージ を表示する.
    攻撃のヒントになるようなエラーメッセージを出さないようにします. 「パスワードが間違っています」などのメッセージは避けます. 「メールアドレスまたはパスワードが間違っています」などとします.
  • パスワード変更処理時には 再認証 を行う.
    パスワード変更処理に限らず重要な処理を行う際には再認証をすることで安全性が高まります. 例えば Amazon では購入履歴を見るだけでも再認証が必要です.
    ※ SQLインジェクション, CSRF の脆弱性があるとパスワードなどを不正に変更される可能性があります. 再認証は CSRF 脆弱性を排除します.
  • パスワード変更処理時には メール通知 を行う.
    パスワード変更処理に限らず重要な処理が完了した際にメール通知をすることで, 第三者による不正な処理や, 意図していない処理が実行されたことに早期に気づくことが出来ます.
  • パスワードリマインダ機能を実装する場合は仕様を吟味する(後述).

また例えば以下のようなことが有効です:

  • ログインの失敗 を監視する.
    パスワード試行攻撃はログイン失敗率が大きくなるので失敗率に従って例えば該当する IP アドレスからの通信を遮断するなどの処置をとります. あるいはログイン失敗が続いた場合, そのアカウントを利用不可能な状態(アカウントロック)にします. これらの制限の発火条件と鎮火条件, そのアカウントへの通知方法を十分に検討する必要があります.
  • HTTPS を利用できる場合は必ずパスワード入力ページとログイン処理を HTTPS にする.
    ページの改ざん, 偽ページが表示されること, パスワードの盗聴を防ぎます.
  • ユーザー登録時のパスワードをサーバーが用意した十分強度のあるパスワード(初期パスワード)とする. 初回ログイン時にユーザーに対して初期パスワードの変更を促します(あるいは強制します).
    ※ 変更した方がいい理由は本人のみがパスワードを知っているべきであること, 初期パスワードを通知するときに初期パスワードが第三者に漏れている可能性があること, サーバーの手元に残っていた初期パスワード(残しておくべきではない)が第三者に漏れる可能性があるのを考慮してのことです.
  • 適当な パスワードの有効期限 を設定する. 有効期限が切れた際にユーザーに対してパスワードの変更を促します(あるいは強制します).
  • 直近でユーザーが設定した パスワードの履歴 を保存し, それらへのパスワードの変更を禁止する. またパスワードの最小有効期間を設定すれば履歴の効果を回避することを防ぐことができます.

パスワードリマインダについて

パスワードリマインダとはパスワードを忘れた場合にもう一度ログインするための方法を提供する機能です. パスワードはハッシュ値で保存するのでパスワードをそのまま通知することは不可能です(ハッシュ化は不可逆関数).

経験上もっとも多くのサイトが採用している手順は以下のようなものです:

  1. パスワードリマインダページでメールアドレスを入力してメール送信ボタンをクリックする.
  2. メール送信完了ページが表示され, メールが届く.
  3. メール本文に記載されたパスワード変更用ページの URL をクリックする.
  4. パスワード変更用ページで新しいパスワードを入力して完了ボタンをクリックする.
  5. パスワードが新しいパスワードに変更される.

メールでパスワード変更用ページの URL を通知する方法なので URL 通知リマインダ と呼ぶことにします.
以下は Twitter のリマインダで届いたメール本文です:

○○○ さん
Twitterのパスワードをお忘れですか?

Twitterアカウント@●●●さんから、パスワードリセットのリクエストがありました。パスワードをリセットするには、以下のボタンをクリックしてください。
Twitterのパスワードをリセットします
または、このURLをコピーしてブラウザーに入力してください。
https://twitter.com/pw_rst/e/(省略)
リクエストしていないにもかかわらず大量のパスワードリセットメールが届くような場合、アカウント設定から、パスワードリセットの際は個人情報の入力を必須とするように変更できます。
詳しくは、こちらのサポートページをご覧ください。

※ Twitter の場合, パスワードの再設定後に連携中のアプリに対して連携の承認を再確認されます.

個人的には以上の方法で十分な気がしますが, 「メールに添付された URL を参照するという習慣を利用者に身につけさせるのは好ましくない」という考え方もあります(徳丸浩, "安全な Web アプリケーションの作り方").

そこで徳丸浩さんの "安全な Web アプリケーションの作り方" では以下の 2 通りの方法が推奨されています:

仮パスワード通知リマインダ
仮パスワードを発行してメールで通知する
確認ナンバー通知リマインダ
パスワード変更画面に直接遷移する

※ 緑色の名称はぼくが勝手に名づけました.

"安全な Web アプリケーションの作り方" では「秘密の質問」に答える工程が存在しますが, 個人的には「秘密の質問」は無意味だと思います. 使ってる側としても秘密の質問の設定を強要されると激しくイライラします.

秘密の質問を排除したそれぞれの処理の流れを以下に示します. 先ほどの URL 通知リマインダとの違いは太字の部分のみです. URL 通知リマインダの流れも違いを比べ易いように併記しておきます.

仮パスワード通知リマインダ

  1. パスワードリマインダページでメールアドレスを入力してメール送信ボタンをクリックする.
  2. メール送信完了ページが表示され, メールが届く.
  3. メール本文に記載された仮パスワードで通常のログイン画面からログインし, パスワード変更用ページを表示する(仮パスワードでログインするとこのページしか表示されないようにする).
  4. パスワード変更用ページで新しいパスワードを入力して完了ボタンをクリックする.
  5. パスワードが新しいパスワードに変更される.

確認ナンバー通知リマインダ

  1. パスワードリマインダページでメールアドレスを入力してメール送信ボタンをクリックする.
  2. 確認番号入力ページが表示され, メールが届く.
  3. メール本文に記載された確認番号を先ほど表示された確認番号入力ページで入力し, OKボタンをクリックしてパスワード変更用ページを表示する.
  4. パスワード変更用ページで新しいパスワードを入力して完了ボタンをクリックする.
  5. パスワードが新しいパスワードに変更される.

URL 通知リマインダ

  1. パスワードリマインダページでメールアドレスを入力してメール送信ボタンをクリックする.
  2. メール送信完了ページが表示され, メールが届く.
  3. メール本文に記載されたパスワード変更用ページの URL をクリックする.
  4. パスワード変更用ページで新しいパスワードを入力して完了ボタンをクリックする.
  5. パスワードが新しいパスワードに変更される.

当然パスワードが変更された時点でそのことを通知するメールを送信します.

URL 通知, 仮パスワード通知, 確認ナンバー通知の各リマインダでの注意点としては, メールアドレスが存在していなくても, 入力したメールアドレスが存在しているかどうかはわからないようにすることです. サイトに登録されているメールアドレスが攻撃者に知られることは避けるべきです.

旧パスワードを無効にするのは新しいパスワードが設定されたのと同時にします.
"安全な Web アプリケーションの作り方" ではなぜか仮パスワード通知リマインダにおいて仮パスワード通知メールを送信したときに旧パスワードを無効にしています. そうする必要はないと思います(何か理由があるのかもしれないですがぼくにはわかりません).