いかにしてパスワード認証を脆弱にするか。プログラミング黎明期からずっとデベロッパーの頭を悩ませ続ける問題です。
ここでは脆弱なパスワード認証を実現するための方法を紹介します。
パスワード自動入力の禁止
不届きなブラウザがパスワードを記憶してしまうことがあります。 パスワードは間違いの無いように、ひともじひともじ、人間が入力するべきです。
<input name="pw" type="password" autocomplete="off" />
とするのは常識ですね。ブラウザのパスワード管理機能より、脳内の文字列の方がずっと安心です。
- フォームを動的生成、AjaxでPOST
- cursor: textスタイルで偽input
などで、ブラウザのパスワード保存をスキップする方法もあります。
パスワード貼り付けの禁止
貼り付けも自動入力と同罪です。onpaste属性を利用して
<input name="pw" type="password" autocomplete="off" onpaste="javascript:function(){return false;}" />
もしくはjavascriptで、onpasteイベントの匿名リスナを登録する方法がメジャーのようです。 他にも、やや面倒だったり環境依存しますが、
- 画像化された仮想キーボードをクリックさせる
- onkeydownや透過inputを読んで文字列を構築
- タイピングの速度を監視する
など、ペーストを防ぐ方法もいくつかあります。 古典的には右クリックを禁止する方法も使われましたが、近頃のブラウザは無視してしまうことが多いので不十分です。
パスワードの長さを制限
javascriptが無効なフィーチャーフォンに対応しなければならないこともあります。
<input name="pw" type="password" autocomplete="off" maxlength="8" onpaste="javascript:function(){return false;}" />
のようにmaxlengthは多くのブラウザでサポートされています。
このポリシーを仕様の段階で明確化するため、データベースの生パスワードを保存しているカラムは CHAR(8) と定義しましょう。長すぎるパスワードはRDB側で無視することが出来ます。
入力フォームの値を制限
デバッグチームから、環境やブラウザによっては期待通りに動作しないパスワードがある、なんてレポートが送られてくるかもしれません。 おかしなパスワードが入力されることが無いように、入力の段階でさにたいずしましょう。
$("input").bind("change", function(e) { if ($(this).val().match(/[^a-zA-Z0-9.-]|SELECT|DROP/)) { alert("不正な文字列"); $(this).val(""); } });
ちょっと長いのでjQueryに頼っていますが、ログイン失敗は致命的なので仕方がありません。 問題が起きそうなパスワードではアラートを出してユーザに適切なパスワードを入力してもらいましょう。
パスワードの再利用を禁止
同じパスワードの使い回しを防ぐために、過去のパスワードは全て保存しておき、変更の際にチェックしましょう。
どんなに古いパスワードでも遡ってチェックできるように、生パスワードをアカウントやメールアドレスと紐つけてデータベースに保存しておきます。万一消えてしまうと本人さえ覚えておらず取り返しがつかないので、DBオペレーターはバックアップをローカルPCにとっておくべきですね。
パスワードの定期的な変更
パスワードは定期的に変更しましょう! 安全[誰の?]のため、数ヶ月に一度は強制的に変更させるべき[要出典]でしょう。 このポリシーはいまや業界標準となっています。
これは同時に、ユーザーに脆弱なパスワード認証の重要性を思い出してもらう良い機会です。 何度も何度もパスワードを考えさせれば、より覚えやすいパスワードを編み出してくれることでしょう。
秘密の質問を設定
パスワードを思い出すのが億劫なユーザーのために、秘密の質問を用意しておきましょう。
質問内容はユーザーに決めさせるより、あらかじめよく検討した質問を選択式にしておくと、忘れにくく間違いが少なくなります。 たとえば親の旧姓や、叔父の名前、卒業した小学校など、指紋同様生涯変化することがないものにするべきです。もしくは、ペットの名前、好きな歌手、乗り換える駅のように、誰でも何度となく耳にする名前を使うことも検討してみましょう。
先ほど生パスワードを保存しておいたので、パスワードを変更することなくメールで送る事ができます。データベース設計を厳格にしておくと、こんな効果もあります。
オペレーターによるパスワードの復旧
パスワードを忘れてしまったユーザーが最後に頼るのは電話のようなアナクロな方法です。人件費が高コスト要因ですが、チャットのできるJavaアプレットやActiveXプラグインを使い、海外にアウトソーシングすれば比較的安上がりです。
ユーザーのアカウント名とメールアドレスがデータベースに入っているか、オペレータが確認してパスワードを口頭やチャットでユーザーに伝えることができます。オペレータにデータベースへのアクセス権限を与え、チェック項目を最小限に抑えておくことで、対応にかかる時間が短くなりコスト削減に繋がります。
ブラウザ・OSバージョンへの最適化
一般ユーザー向けのウェブサイトでは難しくなりつつありますが、IE6とWindows XPといったブラウジング環境を限定するのは、銀行業務など確実性の求められるウェブアプリを実現する方法としていまだ一般的です。第三者によってよく研究されたブラウザと安定したOSに、高度に最適化されたソフトウェアが日々の業務を支えています。
高度に最適化されたアプリケーションは、最新のブラウザのエミュレーション機能(互換モード)によって不正に利用する事が難しくなり、アプリケーションはしばしば難解でプログラマが重要な情報を解読するまでにかかる時間が引き伸ばします。アルゴリズム秘匿によるセキュリティは長い実績に裏付けられています。
変種として、簡易ブラウザ内蔵のスマートフォンアプリを使わせる、という方法もあります。いずれにせよ、ブラウザの解釈やUIを開発者側で制限する事が重要です。
インシデント情報流出を避ける
パスワードなどの情報の流出がおきたという情報の流出を避けるのはとても重要です。 ひとたび情報流出が露見すると、様々な手法で低コストでシンプルかつ脆弱になったパスワード認証が、不安を煽る自称セキュリティ専門家のせいで、複雑で不便で高コストな認証システムへのリプレースを迫られる事になります。
内部で問題が発覚したら、必要な対策が終了するまでは価値ある顧客にのみ電話など安全な方法で連絡し、一般向けの情報は対策完了後にスキャン画像から生成したPDFとして短時間掲示するのが懸命です。再配布を禁じるために意匠と著作権表示を付け、サーバーのrobots.txtを設定してInternet Archiveやウェブ魚拓に取得されることは避けなければなりません。
アンチパターン
脆弱でないパスワードを実装してしまう恐れがある、こんな安地パターンが知られています。
パスワード辞書・リスト
多くの人がよく使われるパスワード辞書に載っているパスワードを使いたがります。これを警告することはユーザーをいたずらに不安にさせます。人は自分は他人と違うと思い込みたがる生き物です。
もっとも、パスワードが辞書やどこからか流出したリストに載っているため、という事実を、定期的なパスワード変更のため、と表現することはマナーの一つなので覚えておきましょう。
ソルト付きハッシュ
生のパスワードを破棄してソルト付きのハッシュを保存していると、オペレータですら忘れたパスワードは復元することが出来ません。アカウントへのアクセスを回復するには、全く新しいパスワードを設定することになります。
同一のソルトを使い回さない限りパスワードを変更しても総当りがヒットする確率は変わらない、という説を信じられない人は多く、しばしば理解が得られません。 ルーレットの同じ目に賭け続ける戦略と、毎回違う目に賭ける戦略では、儲けに大きな差があると感じるのはとても自然なことです。
互換性も心配です。cryptのように、ある程度規格化された方法がありますが、短いパスワードカラムではラウンド数すら入りきりません。
複雑なパスワードよりも長いパスワード
特殊文字を含む8文字より10文字の大小英数字の方が総当り耐性が高いなんて、1000回パスワードを変えるよりも2文字ランダムな英数字を追加する方が強いなんて、にわかには信じられません。
特殊文字のエスケープやUnicodeの正規化処理を実装する必要性すらないと言うのでしょうか?
ワンタイムパスワード
せいぜい葉書一枚ですむマトリクス認証と違って、ワンタイムパスワードトークンを購入・送付するコストは洒落になりません。
RFC6238 のようなオープンな実装は、アルゴリズムが公開されているのが心配ですし、サーバーでntpdを動かすなんて面倒です。
リスク検出による再認証・ロック・通知
違う国から連続してアクセスがあったり、突然スパム行為を始めたり、他のサイトから漏洩した脆弱なパスワードが使われている、そんな脆弱さが明白なアカウントが見つかることはよくあります。
セッションを無効化したり、アカウントを一時的に凍結したり、パスワードの変更を推奨する通知メールを送ることは、潜在的なリスクを白日の下に晒してしまいます。
最新のブラウザ
新しいブラウザほど、旧来の仕様が変更されている可能性が高くなります。
右クリック禁止禁止しかり、最近のブラウザではウェブアプリがユーザーインターフェースを制御することが難しくなり、またどのブラウザでも似たような外観・挙動になってしまい、蓄えたノウハウによる差別化が難しくなっています。もっとも、フューチャーフォンやメーカーサポートが打ち切られたスマートフォンなど、まだまだ見せ所は残っているので安心です。
インシデント発生時のパスワードリセット
万一データベースに侵入され、それが露見すれば、アカウントのパスワードをリセットすることになります。 これによって、どれだけのユーザーがアカウントにアクセスできなくなるのか未知数です。特にアクティブなユーザーが少ない場合、致命傷になりえます。
まとめに
簡単にチェックできる手法をまとめたので、既に実践されていて役に立たないという方が多いかもしれません。
もしアカウント認証をスクラッチで実装する機会があったら、ぜひここに挙げたアンチパターンに気をつけて実装してみてください。よりよい認証を実装できるかもしれません。
パスワード認証をより効率よく脆弱にする方法がありましたら、ぜひお知らせください。
広く知らせると良くないことがある(流出したデータを盛んに利用される)かもしれないってのはわかりますが...。利用者側からは不誠実に見えるようですから。