ウェブアプリケーションを攻撃者から守るためのロードマップ
ウェブアプリのセキュリティ対策について無責任なことをネットで書くことはご法度であり、また、セキュリティ対策を講じる担当者もネット上の情報を安易に受け止めてはいけない。このドキュメントも例外ではない。
JPAが公開している安全なウェブサイトの作り方、脆弱性情報データベースの情報をこまめにチェックすること。
ネットワーク構成とファイアウォール
アプリケーションとストレージは分離し、HTTPを受け付けるサーバーとそのportのみをpublicに晒す。ロードバランサを利用するのであれば、LBの80,443番ポートのみpublicに晒し、APPとDBはローカルネットワーク内部のみのトラフィックを受け付ける。
各サーバにアクセスするために、マネジメント用のサーバMGMTを設けると良い。その場合の構成は、LB(80,443ポートを晒す),MGMT(カスタマイズされたsshポートを晒す),APP(ローカルのみ),DB(ローカルのみ)
中間者攻撃
クラウドサーバへの初回SSHアクセス時は、予めクラウド基盤で提供されている該当サーバのフィンガープリントを照合する。
たとえ運用チームだけが用いるウェブコンソールであっても、クラウド上など社内ネットワークの外に置かれている以上は必ず中間者攻撃を想定した構成にしなければならない。対策として最も優れている方法は「とにかくLet's Encryptを入れて中間者攻撃を確実に防ぐ」こと。
サーバー本体のアップデート
HTTPサーバーおよびリバースプロキシのセキュリティパッチは必ず適用する。
無停止で手軽にメンテナンスできるような構成が望ましいため、グローバルIPをサーバにほぼゼロダウンタイムでアタッチできるサービスを利用すると良い。
認証
sshログインはpublic key認証とパスワード認証を併用することが望ましい。
アプリへのログインに用いられるパスワードは必ずsaltを付けてハッシュ化する。saltは共通のものだけを使うのではなく、レコード毎に生成しなければならない。
システム運用チームは、特権的なウェブコンソールでアカウントを共有してはならない。運用チームのメンバーが退職した際にはそのアカウントをログイン不能な状態にするか、可能であれば削除すること。
外部システムとの連携に用いられる認証情報は、アプリへのログインに用いる情報とは完全に分離し、APIアクセスキーやToken等として簡単に失効させることができるようにすること。
PaaS等を利用する際に用いられるクレデンシャルを環境変数に設定する場合、その環境変数がプリントされるページが存在してはならない(例:AWS S3の認証情報をHTTPサーバの環境変数にセットした状態で、phpinfo()が呼ばれるページをプロダクションに放置してしまうなど)
ソースコード中に外部システムのクレデンシャルを直接書き込まないこと。
ウェブアプリ固有の問題
認証情報をhiddenパラメタに含めてはいけない。
適切なHTMLエスケープ機構を利用せずにユーザーからの入力値やDBから読みだした値を出力することはXSSに繋がる。
副作用のある操作は全てCSRF対策が必要。
顧客自身がページをWYSIWYGエディタ等でカスタマイズできる機能がある場合、それ自体がXSS脆弱性となっており、さらにページ編集機能にCSRF対策が施されていない場合はフィッシングサイトへの誘導に利用される危険性がある。
決済機能
クレジットカード番号などは間違ってもサーバに保存してはならない。
デバッグ目的でカード番号をログに保存してはいけない。
決済時のみ決済サービスへjumpする機能を利用することができるのであれば、利用する。
何らかの攻撃が成功し決済サービスへのjump先がフィッシングサイトへ書き換えられてしまう事態を想定し、システムの外から定期的に(数分間隔)決済ページへのリダイレクト先を監視するサーバを立てること(具体的には、決済ページに遷移するまでのアクセスを順次実行するUserAgentを作ってcronで定期的に実行して監視。)
SQLインジェクション
何らかの値をリテラル・変数問わず入力する場合、必ずプリペアードステートメントを利用する。プリペアードステートメントのエミュレーションはやはりエミュレーションに過ぎないので安全ではない。
プリペアードステートメントを利用しなくても安全なケースは「select count(*) from table_a」等、「かならずこのSQLにしかならない」ということが確定している定型文などに限ると考える。
原則として、「本物の(エミュレーションではない)プリペアードステートメントを利用すること以外にSQLインジェクションの有効な対策は存在しない」と考えるべきである。
二段階(二要素)認証とアクティビティ通知
アプリケーションの利用者が必ずしも高いリテラシーを持っているとは限らない。パスワードは辞書攻撃等で突破される前提で考えておき、二段階(二要素)認証を必ず導入しておくこと。
異常な回数のログイン試行やログインの成功などを、事前に登録されたアカウント本人のメールアドレス等にそのアクティビティを送る。
パスワードリセット時に、事前に登録されたもの以外のメールアドレスに復旧URLを送り付けてはいけない(例:7Pay)