セキュリティガイド
Play フレームワークは、セキュリティを念頭に置いて設計されています - しかし、開発者が Play フレームワークを誤って使い、セキュリティホールを開けてしまうことを防ぐのは不可能です。このガイドはウェブアプリケーションにおけるセキュリティ上の一般的な問題と、Play アプリケーション でどのようにしてそれらを避けるかについて説明します。
セッション
ユーザに関連する情報、特にログイン状態を保つ必要があることが、よくあります。セッションがなければ、ユーザは、リクエスト毎に認証照明を送信しなければなりません。
セッションは、これら: ユーザのブラウザに保存される、ユーザをウェブサイトで特定するクッキーのセット、ウェブアプリケーションが保存先にデータ層よりもむしろセッションを選ぶかもしれない、例えば言語情報のために存在します。
秘密鍵は… 秘密のまま
セッションは、署名はされていますが暗号化はされていないキー/値のハッシュです。これは、秘密鍵が安全である限り、第三者がセッションを捏造できないことを意味します。
秘密鍵は conf/application.conf に保存されます。これを自分だけのものとして保持することは非常に重要です: 公開されたリポジトリにこれをコミットしてはいけませんし、他の誰かによって書かれたアプリケーションをインストールするときには、秘密鍵を自分自身の物に変更しなければなりません。これは、“play secret” コマンドで行うことができます。
重要なデータは保存しない
とは言え、セッションは暗号化されていないので、セッションに重要なデータを保存するべきではありません。LAN や Wi-Fi 上の接続を盗聴することで、これらのデータをユーザクッキー上に見ることができます。
セッションはクッキーの中に保存され、クッキーは 4KB に制限されます。この制限に加え、保存できるのは文字列のみです。
クロスサイトスクリプティング
クロスサイトスクリプティングはウェブアプリケーションでもっとも一般的な脆弱性の 1 つです。XSS は、アプリケーションが提供するフォームを使用することで悪意ある JavaScript をウェブページに挿し込むことで成立します。
誰でもコメントできるブログシステム書いているとしましょう。この HTML ページに訪問者が書き込む内容を盲目的に受け入れる場合、このサイトは攻撃者に対して開かれています。攻撃は以下のように行われます:
- 訪問者にポップアップを表示します
- 攻撃者によって制御されたサイトに訪問者をリダイレクトします
- 現在の訪問者にのみが閲覧できるべきであった情報が盗まれ、攻撃者のサイトに送信されます
したがって、これらの攻撃からアプリケーションを保護するのは非常に重要です。
バージョン 1.0.1 以降、Play のテンプレートエンジンは自動的に文字列をエスケープします。そのためには、application.conf で escapeInTemplates が有効にされていることを確認する必要があります。新規に作成されたアプリケーションでは、これはデフォルトで設定されます。アプリケーションが 1.0.1 より前のリリースで作成されているな場合、これは有効に設定されていないかもしれません。
future.escapeInTemplates=true
このオプションを有効にすることを強くお勧めします。エスケープしない HTML をテンプレートに挿入する必要が本当にある場合、その文字列に対して raw() メソッドを使用することができます。しかし、文字列がユーザ入力から来る場合は、まず初めにサニタイジンズされることを確認する必要があります。
ユーザ入力をサニタイジングするときは、常に (安全でないタグを禁止し、それ以外を許可する) ブラックリスト方式よりも、(安全なタグのみを許可する) ホワイトリスト方式を優先してください。
SQL インジェクション
SQL インジェクションは、開発者が意図しない SQL クエリを実行するユーザ入力を使用することで成立する脆弱性です。データを破壊したり、または現在のユーザにとって見えるべきでないデータにアクセスするために使用されます。
高レベルの “find” メソッドを使用する場合、SQL インジェクションに対応するべきです。手動でクエリを作成する場合、+ で文字列を連結するのではなく、“?” マークを使用するよう注意深くあるべきです。
これは良いです:
createQuery("SELECT * from Stuff WHERE type= ?1").setParameter(1, theType);
これは良くありません:
createQuery("SELECT * from Stuff WHERE type=" + theType;
クロスサイトリクエストフォージェリ
CSRF は、ウェブアプリケーションにおいて真の問題になる場合があります:
この攻撃手法は、ユーザが認証されていると信じている web アプリケーションにアクセスするページに悪意あるコードかリンクを含めることで実行されます。この web アプリケーションのセッションがタイムアウトされていない場合、攻撃者は認証されていない命令を実行できる場合があります。
この攻撃を防ぐために最初にすることは、GET と POST メソッドを適切に使用することです。これは、アプリケーションの状態を変更に使用されるのは POST メソッドだけであるべきということを意味します。
POST リクエストをセキュアで重要なアクションとして適切に保証する唯一の方法は、認証トークンを発行することです。現在の Play 1.0.2 にはこれを扱う組み込みのヘルパがあります:
- 新しい checkAuthenticity() メソッドがコントローラで利用可能であり、これはリクエストパラメータ中に有効な認証トークンが存在するかどうかチェックし、何かが正しくない場合は、認証不可のレスポンスを送信します。
- session.getAuthenticityToken() は、現在のセッションにおいてのみ有効な認証トークンを生成します。
- #{authenticityToken/} は、どのようなフォームにも加えることができる hidden 入力フィールドを作成します。
例えば、以下のようにすると:
public static destroyMyAccount() {
checkAuthenticity();
...
}
適切な認証トークンを含むフォームから呼ばれた場合にのみ動作します:
#{form @ destroyMyAccount()}
#{authenticityToken /}
<input type="submit" value="destroy my account">
#{/form}
もちろん、コントローラ階層におけるすべてのアクションを保護したい場合は、これを事前フィルタとして追加することができます。