なぜPHPアプリにセキュリティホールが多いのか?

【スクリプトインジェクション対策18】ログイン処理を正しく実装する

言うまでもなくログイン処理はセキュリティ上最も重要な処理です。しかし、ログイン処理が正しく行われていないサイトも少なくありません。例えば、PayPalのログインページがスクリプトインジェクションに脆弱であったことは有名です。ログインページがスクリプトインジェクションに脆弱であると、簡単にユーザ名とパスワードを盗めてしまいます。PayPalのログインページはHTTPSで保護されていましたが、スクリプトインジェクションに脆弱ではHTTPSは何の役にも立ちません。

ログインページがスクリプトインジェクションに脆弱であるのは論外であるとしても、ログインの実装方法が正しくない場合が少なくありません。ログインに成功した場合、新しいセッションIDを割り当てるべきです。これは、セッションIDを頻繁に変更するのエントリでも解説したように、セッションIDの盗聴や固定化などから防御するために行います。

Webサイトによってはログインした後、どこでログオフすればよいのか分かり難いサイトもあります。安全性を重視するならサイトの利用が終わったらすぐにログオフしたほうがよいです。ログインしたままのユーザが多いからといってログオフの機能を実装しなかったり、分かり難い場所にログオフ用のリンク/ボタンを配置するのは好ましくありません。言うまでもなくログオフしたらセッションIDとセッション情報は破棄し、必要であれば新しいセッションIDを割り当てます。

すべてのWebアプリケーションは少なくとも最後に何時のどのIPアドレス(できればDNSを逆引きも行いホスト名も)からログインしたか確認できるようにすべきです。この機能を実装するだけで軽い悪戯目的の成りすましを思いとどまらせることが可能となる確率が高まります。ユーザ自身が不正ログインに気付けば、パスワードの変更やデータ改ざんの確認等、適切な対応を行える確率も高くなります。セキュリティが重要なWebアプリケーションでは過去数回のログイン履歴も参照できるようにするとよりよいでしょう。

自動ログインは正しく実装されていないアプリケーションが非常に多いです。自動ログインを実装しない。実装する場合は正しく実装するでも解説したとおり、自動ログインを実装する場合、自動ログイン用の鍵となるクッキーをパス付きで保管すべきです。鍵には必ず十分に長いランダムな文字列(/dev/urandomを利用したSHA1ハッシュなど)を利用し、ログインのたびに新しい鍵を設定します。一度使った鍵は無効となるようにデータベース等から削除します。

決してユーザ名とパスワードをハッシュ化した値などを自動ログイン用の鍵に使用してはなりません。笑いごとのように思われるかも知れませんが、過去にはBASE64でエンコードしたユーザ名とパスワードを自動ログイン用の鍵に設定していたWebアプリケーションもありました。BASE64は可逆変換可能なエンコード方式なので平文と変わりません。

Webサーバが標準でサポートしていることが多いBASIC認証はBASE64でエンコードしたユーザ名とパスワードをリクエストの度に送信しています。このことからも分かるようにBASIC認証は非常に危険な認証方式です。通常は使用してはならない認証方式であることを覚えておいてください。

固定化した自動ログインキーを採用しているシステムは、セッションIDが固定化しているよりも脆弱です。固定や固定に近い自動ログインキーは絶対に使用してはなりません。必ずランダム文字列ソース(/dev/urandom等)からSHA1ハッシュ値等を取得してその値を利用します。

盗聴などによりパスワードが漏洩してしまうと、そのアカウントが長期間不正利用される可能性があります。JavaScriptが有効なクライアントであれば、平文のパスワードを送信するのではなく、平文のパスワードがネットワーク上に流れないチャレンジレスポンス型の認証を行ったほうが安全です。HTTPSプロトコルが利用できる場合はHTTPSプロトコルを利用したほうがより安全であることには言うまでもありません。

チャレンジレスポンス認証のフロー

  1. サーバ側で毎回ランダムに設定されたチャレンジ文字列(これも通常はランダム文字列のSHA1ハッシュなどを利用する)をクライアントに送信する
  2. クライアント側でJavaScriptが有効な場合、ユーザが入力したパスワードとチャレンジ文字列のハッシュ値(SHA1など。この値がレスポンスとなる)を計算し、この値をログインフォーム送信時に送信する。このとき、平文のパスワードを送信してしまうと意味がないので平文のパスワードは送信しないようにする
  3. サーバ側でパスワード値とチャレンジ文字列のハッシュ値を計算し、クライアントから送信されたレスポンスと比較する。一致した場合はユーザが正しいパスワードを入力したことを意味し、異なる場合は間違ったパスワードを入力したことが分かる

この方法はJavaScriptが有効なブラウザでなければ利用できないので、JavaScriptが無効なブラウザでは平文のパスワードが利用できるように作っておくとよりよいでしょう。

多くのWebサーバがサポートしているDIGEST認証もチャレンジレスポンス型の認証方式です。BASIC認証に比べるとかなり安全性は向上していますが、ログオフが正式に規定されていない、ログイン画面を自由に制御できない、クッキーを利用したセッション管理のようにセッションIDの更新ができないなど問題も多いので本格的なWebアプリケーションでは利用されません。

対策のまとめ

  • ログインしたら新しいセッションIDを発行する
  • ログオフしたらセッションを破棄する
  • 前回のログインIPや時間を表示する
  • セキュリティを重視するなら自動ログインは実装しない
  • ログイン中のセッションの有効期限は短くする
  • セッションIDをCSRF対策に利用しない
  • セッションIDの管理を正しく行う
  • ログインページにはHTTPSプロトコルを利用する
  • チャレンジレスポンス型の認証を行う

おすすめ記事

記事・ニュース一覧