CSRF対策 ================================================================================ .. only:: html .. contents:: 目次 :local: Overview -------------------------------------------------------------------------------- | Cross site request forgeries(以下、CSRFと略す)とは、Webサイトにスクリプトや自動転送(HTTPリダイレクト)を実装することにより、 | ユーザが、ログイン済みの別のWebサイト上で、意図しない何らかの操作を行わせる攻撃手法のことである。 | サーバ側でCSRFを防ぐには、以下の方法が知られている。 * 秘密情報(トークン)の埋め込み * パスワードの再入力 * Referのチェック | OWASPでは、トークンパターンを使用する方法が\ `推奨されている。 `_\ .. figure:: ./images/csrf_check_other_site.png :alt: csrf check other site :width: 60% :align: center **Picture - csrf check other site** .. note:: **OWASPとは** Open Web Application Security Projectの略称であり、信頼できるアプリケーションや、セキュリティに関する 効果的なアプローチなどを検証、提唱する、国際的な非営利団体である。 https://www.owasp.org/index.php/Main_Page | CSRFを回避する方法は、前述したように複数あるが、固定トークンを使用するライブラリを、Spring Securityが提供している。 | セッション毎に1つの固定トークンを用い、すべてのリクエストについて、同じ値を使用している。 | デフォルトではHTTPメソッドが、GET,HEAD,TRACE,OPTIONS以外の場合、 | リクエストに含まれるCSRFトークンをチェックし、値が一致しない場合は、エラー(HTTP Status:403[Forbidden])とする。 .. figure:: ./images/csrf_check_kind.png :alt: csrf check other kind :width: 50% :align: center **Picture - csrf check other kind** .. tip:: CSRFトークンチェックは、別サイトからの不正な更新リクエストをチェックし、エラーとするものである。 ユーザに順序性(一連の業務フロー)を守らせ、チェックするためには、\ :ref:`double-submit_transactiontokencheck`\ を参照されたい。 .. warning:: **マルチパートリクエスト(ファイルアップロード)時におけるCSRF対策** ファイルアップロード時のCSRF対策については、\ :ref:`ファイルアップロード Servlet Filterの設定 `\ を留意されたい。 | How to use -------------------------------------------------------------------------------- Spring Securityの設定 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Spring SecurityのCSRF機能を使用するための設定を説明する。 \ :ref:`Spring Security の How to use `\ で設定したweb.xmlを前提とする。 .. _csrf_spring-security-setting: spring-security.xmlの設定 """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" 追加で設定が必要な箇所を、ハイライトしている。 .. code-block:: xml :emphasize-lines: 3-4,8- .. tabularcolumns:: |p{0.10\linewidth}|p{0.90\linewidth}| .. list-table:: :header-rows: 1 :widths: 10 90 * - 項番 - 説明 * - | (1) - | \ ````\ 要素に\ ````\ 要素を定義することで、Spring Security のCSRFトークンチェック機能を利用できるようになる。 | デフォルトでチェックされるHTTPメソッドについては、\ :ref:`こちら`\ を参照されたい。 | 詳細については、\ `Spring Securityのレファレンスドキュメント `_\ を参照されたい。 * - | (2) - | \ ``AccessDeniedException``\ を継承したExceptionが発生した場合、Exceptionの種類毎に表示するviewを切り替えるためにHandlerを定義する。 | 全て同じ画面で良い場合は ``error-page`` 属性に遷移先のjspを指定することで可能となる。 | Spring Securityの機能でハンドリングしない場合は、\ :ref:`こちら`\ を参照されたい。 * - | (3) - | エラーページを切り替えるためにSpring Securityで用意されているHandlerのclassに \ ``org.springframework.security.web.access.DelegatingAccessDeniedHandler``\ を指定する。 * - | (4) - | コンストラクタの第1引数でデフォルト以外のException(\ ``AccessDeniedException``\ を継承したException)の種類毎に表示を変更する画面をMap形式で設定する。 * - | (5) - | keyに \ ``AccessDeniedException``\ を継承したException を指定する。 | 実装クラスとして、Spring Securityで用意されている \ ``org.springframework.security.web.access.AccessDeniedHandlerImpl`` を指定する。 | propertyのnameにerrorPageを指定し、valueに表示するviewを指定する。 * - | (6) - | (5)とExceptionの種類が違う場合に表示の変更を定義する。 * - | (7) - | コンストラクタの第2引数でデフォルト(\ ``AccessDeniedException``\ とコンストラクタの第1引数で指定していない\ ``AccessDeniedException``\を継承したException)の場合のviewを指定する。 * - | (8) - | 実装クラスとして、Spring Securityで用意されている \ ``org.springframework.security.web.access.AccessDeniedHandlerImpl`` を指定する。 | propertyのnameにerrorPageを指定し、valueに表示するviewを指定する。 | .. tabularcolumns:: |p{0.40\linewidth}|p{0.60\linewidth}| .. list-table:: \ ``AccessDeniedException``\ を継承したCSRF対策により発生するExceptionの種類 :header-rows: 1 :widths: 40 60 * - Exception - 発生理由 * - | org.springframework.security.web.csrf. | InvalidCsrfTokenException - | クライアントからリクエストしたCSRFトークンとサーバで保持しているCSRFトークンが一致しない場合に発生する。 * - | org.springframework.security.web.csrf. | MissingCsrfTokenException - | CSRFトークンがサーバに存在しない場合に発生する。 | デフォルトの設定ではCSRFトークンをHTTPセッションに保持するため、CSRFトークンが存在しないということはHTTPセッションが破棄された(セッションタイムアウトが発生した)ことを意味する。 | | \ ````\ 要素の \ ``token-repository-ref``\ 属性でCSRFトークンの保存先をキャッシュサーバやDBなどに変更した場合は、CSRFトークンを保存先から削除した場合に\ ``MissingCsrfTokenException``\ が発生する。 | これは、トークンの保存先をHTTPセッションにしていない場合は、本機能を使ってセッションタイムアウトの検知が出来ない事を意味している。 .. note:: CSRFトークンの保存先としてHTTPセッションを使用する場合は、 CSRFトークンのチェック対象のリクエストに対してセッションタイムアウトを検出することができる。 セッションタイムアウト検知後の動作は、\ ````\ 要素の\ ``invalid-session-url``\ 属性の指定によって異なる。 * \ ``invalid-session-url``\ 属性の指定がある場合は、セッションを生成した後に\ ``invalid-session-url``\ に指定したパスへリダイレクトされる。 * \ ``invalid-session-url``\ 属性の指定がない場合は、\ ````\ 要素に指定した\ ``org.springframework.security.web.access.AccessDeniedHandler``\ の定義に従ったハンドリングが行われる。 CSRFトークンのチェック対象外のリクエストに対してセッションタイムアウトを検出する必要がある場合は、 \ ````\ 要素の\ ``invalid-session-url``\ 属性を指定して検出すればよい。 詳細は、「:ref:`authentication_session-timeout`」を参照されたい。 | .. _csrf_403-webxml-setting: .. note:: **の設定を省略した場合のエラーハンドリングについて** web.xmlに以下の設定を行うことで、任意のページに遷移させることができる。 **web.xml** .. code-block:: xml 403 /WEB-INF/views/common/error/accessDeniedError.jsp .. tabularcolumns:: |p{0.10\linewidth}|p{0.90\linewidth}| .. list-table:: :header-rows: 1 :widths: 10 90 * - 項番 - 説明 * - | (1) - | error-code要素に、ステータスコード403を設定する。 * - | (2) - | location要素に、遷移先のパスを設定する。 .. _csrf_change-httpstatus403: .. note:: **ステータスコード403以外を返却したい場合** リクエストに含まれるCSRFトークンが一致しない場合、ステータスコード403以外を返却したい場合は、\ ``org.springframework.security.web.access.AccessDeniedHandler``\ インタフェースを 実装した、独自のAccessDeniedHandlerを作成する必要がある。 .. _csrf_spring-mvc-setting: spring-mvc.xmlの設定 """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" CSRFトークン用の\ ``RequestDataValueProcessor``\ 実装クラスを利用し、Springのタグライブラリの\ ````\ タグを使うことで、自動的にCSRFトークンを、hiddenに埋め込むことができる。 .. code-block:: xml :emphasize-lines: 1-2,5-6 .. tabularcolumns:: |p{0.10\linewidth}|p{0.90\linewidth}| .. list-table:: :header-rows: 1 :widths: 10 90 * - 項番 - 説明 * - | (1) - | \ ``org.terasoluna.gfw.web.mvc.support.RequestDataValueProcessor``\ を複数定義可能な | \ ``org.terasoluna.gfw.web.mvc.support.CompositeRequestDataValueProcessor``\ をbean定義する。 * - | (2) - | コンストラクタの第1引数に、\ ``org.springframework.security.web.servlet.support.csrf.CsrfRequestDataValueProcessor``\ のbean定義を設定する。 .. note:: CSRFトークンの生成及びチェックは \ ````\ の設定で有効になる \ ``CsrfFilter``\ により行われるので、開発者はControllerで特にCSRF対策は意識しなくてよい。 .. _csrf_form-tag-token-send: フォームによるCSRFトークンの送信 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ JSPで、フォームからCSRFトークンを送信するには * \ ````\ タグを使用してCSRFトークンが埋め込まれた\ ````\ タグを自動的に追加する * \ ````\ タグを使用してCSRFトークンが埋め込まれた\ ````\ タグを明示的に追加する のどちらかを行う必要がある。 .. _csrf_formformtag-use: CSRFトークンを自動で埋め込む方法 """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" \ :ref:`spring-mvc.xmlの設定`\ の通り、\ ``CsrfRequestDataValueProcessor``\ が定義されている場合、 \ ````\ タグを使うことで、CSRFトークンが埋め込まれた\ ````\ タグが、自動的に追加される。 JSPで、CSRFトークンを意識する必要はない。 .. code-block:: jsp 以下のようなHTMLが、出力される。 .. code-block:: html
.. tabularcolumns:: |p{0.10\linewidth}|p{0.90\linewidth}| .. list-table:: :header-rows: 1 :widths: 10 90 * - 項番 - 説明 * - | (1) - | Spring Securityのデフォルト実装では、\ ``name``\ 属性に\ ``_csrf``\ が設定されている \ ````\ タグが追加され、CSRFトークンが埋め込まれる。 CSRFトークンはログインのタイミングで生成される。 .. tip:: Spring 4上で\ ``CsrfRequestDataValueProcessor``\ を使用すると、 \ ````\ タグの\ ``method``\ 属性に指定した値がCSRFトークンチェック対象のHTTPメソッド(Spring Securityのデフォルト実装ではGET,HEAD,TRACE,OPTIONS以外のHTTPメソッド)と一致する場合に限り、 CSRFトークンが埋め込まれた\ ````\ タグが出力される。 例えば、以下の例のように \ ``method``\ 属性にGETメソッドを指定した場合は、 CSRFトークンが埋め込まれた\ ````\ タグは出力されない。 .. code-block:: jsp <%-- ... --%> これは、\ `OWASP Top 10 `_\ で説明されている、 The unique token can also be included in the URL itself, or a URL parameter. However, such placement runs a greater risk that the URL will be exposed to an attacker, thus compromising the secret token. に対応している事を意味しており、セキュアなWebアプリケーション構築の手助けとなる。 .. _csrf_formtag-use: CSRFトークンを明示的に埋め込む方法 """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" \ ````\ タグを使用しない場合は、明示的に、\ ````\ タグを追加する必要がある。 \ ````\ タグを使用すると、CSRFトークンが埋め込まれた\ ````\ タグが出力される。 .. code-block:: jsp
以下のようなHTMLが、出力される。 .. code-block:: html
.. tabularcolumns:: |p{0.10\linewidth}|p{0.90\linewidth}| .. list-table:: :header-rows: 1 :widths: 10 90 * - 項番 - 説明 * - | (1) - | CSRFトークンが埋め込まれた\ ````\ タグを出力するために、\ ````\ タグを指定する。 * - | (2) - | Spring Securityのデフォルト実装では、\ ``name``\ 属性に\ ``_csrf``\ が設定されている \ ````\ タグが追加され、CSRFトークンが埋め込まれる。 .. _csrf_default-add-token-method: .. note:: CSRFトークンチェック対象のリクエスト(デフォルトでは、HTTPメソッドが、GET, HEAD, TRACE, OPTIONS以外の場合)で、CSRFトークンがない、または サーバー上に保存されているトークン値と、送信されたトークン値が異なる場合は、\ ``AccessDeniedHandler``\ によりアクセス拒否処理が行われ、HttpStatusの403が返却される。 \ :ref:`spring-security.xmlの設定 `\ を記述している場合は、指定したエラーページに遷移する。 .. _csrf_ajax-token-setting: AjaxによるCSRFトークンの送信 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | \ ````\ の設定で有効になる \ ``CsrfFilter``\ は、前述のようにリクエストパラメータからCSRFトークンを取得するだけでなく、 | HTTPリクエストヘッダーからもCSRFトークンを取得する。 | Ajaxを利用する場合はHTTPヘッダーに、CSRFトークンを設定することを推奨する。JSON形式でリクエストを送る場合にも対応できるためである。 .. note:: HTTPヘッダ、リクエストパラメータの両方からCSRFトークンが送信する場合は、HTTPヘッダの値が優先される。 | \ :doc:`../ArchitectureInDetail/Ajax`\ で使用した例を用いて、説明を行う。追加で設定が必要な箇所を、ハイライトしている。 **jspの実装例** .. code-block:: jsp :emphasize-lines: 3-4 .. code-block:: jsp :emphasize-lines: 3-7 .. tabularcolumns:: |p{0.10\linewidth}|p{0.90\linewidth}| .. list-table:: :header-rows: 1 :widths: 10 90 * - 項番 - 説明 * - | (1) - | \ ````\ タグを設定することにより、デフォルトでは、以下の\ ``meta``\ タグが出力される。 * \ ````\ * \ ````\ * \ ````\ (\ ``content``\ 属性の値はランダムなUUIDが設定される) * - | (2) - | \ ````\ タグに設定されたCSRFトークンを取得する。 * - | (3) - | \ ````\ タグに設定されたCSRFヘッダ名を取得する。 * - | (4) - | リクエストヘッダーに、\ ````\ タグから取得したヘッダ名(デフォルト:X-CSRF-TOKEN)、CSRFトークンの値を設定する。 * - | (5) - | この書き方はXSSの可能性があるので、実際にJavaScriptコードを書くときは気を付けること。 | 今回の例では\ ``data.addResult``\ 、\ ``data.subtractResult``\ 、\ ``data.multipyResult``\ 、\ ``data.divideResult``\ の全てが数値型であるため、問題ない。 JSONでリクエストを送信する場合も、同様にHTTPヘッダを設定すればよい。 .. todo:: .. note:: **ステータスコード403以外を返却したい場合** リクエストに含まれるCSRFトークンが一致しない場合に、ステータスコード403以外を返却したい場合は、\ ``org.springframework.security.web.access.AccessDeniedHandler``\ インタフェースを実装した、独自のAccessDeniedHandlerを作成する必要がある。