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対策機能は、Spring Security3.2から提供される機能であるが、共通ライブラリ(terasoluna-gfw-security-web)の1.0.0.RELEASE版が依存している Spring Securityのバージョンは、3.1.4.RELEASEである(共通ライブラリの1.0.0.RELEASE版リリース時には、Spring Securityの3.2.0.RELEASE版は未リリースであるため)。 このため、terasoluna-gfw-security-webプロジェクト内に、1.0.0.RELEASE版リリース時のSprinng SecurityのCSRF対策機能に関する以下のクラスが同梱されている。 * org.springframework.security.web.authentication.session.CompositeSessionAuthenticationStrategy * org.springframework.security.web.csrf.CsrfAuthenticationStrategy * org.springframework.security.web.csrf.CsrfFilter * org.springframework.security.web.csrf.CsrfLogoutHandler * org.springframework.security.web.csrf.CsrfToken * org.springframework.security.web.csrf.CsrfTokenRepository * org.springframework.security.web.csrf.DefaultCsrfToken * org.springframework.security.web.csrf.HttpSessionCsrfTokenRepository * org.springframework.security.web.csrf.InvalidCsrfTokenException * org.springframework.security.web.servlet.support.csrf.CsrfRequestDataValueProcessor 共通ライブラリのバージョンアップのタイミングで、Spring Securityのバージョンアップし、上記のクラスは、terasoluna-gfw-security-webプロジェクトからは取り除かれる予定である。 | 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-6,10- .. tabularcolumns:: |p{0.10\linewidth}|p{0.90\linewidth}| .. list-table:: :header-rows: 1 :widths: 10 90 * - 項番 - 説明 * - | (1) - | \ ````\ 要素を定義し、\ ``org.springframework.security.web.authentication.logout.LogoutFilter``\ の前に CSRFのFilter定義を行う。 * - | (2) - | \ ````\ 要素の、\ ``session-authentication-strategy-ref``\ 属性で、 | \ ``org.springframework.security.web.authentication.session.SessionAuthenticationStrategy``\ を参照する。 * - | (3) - | \ ``org.springframework.security.web.csrf.CsrfFilter``\ のbean定義を行う。 * - | (4) - | コンストラクタの第1引数で、トークンの作成、保持を行う\ ``org.springframework.security.web.csrf.CsrfTokenRepository``\ を参照する。 * - | (5) - | \ ``accessDeniedHandler``\ プロパティに\ ``org.springframework.security.web.access.AccessDeniedHandlerImpl``\ を bean定義する。 * - | (6) - | \ ``AccessDeniedHandlerImpl``\ の\ ``errorPage``\ プロパティに、リクエストに含まれるCSRFトークンが、一致しない場合の遷移先パスを設定する。 | 設定を省略した場合、リクエストに含まれるCSRFトークンが一致しない場合、ステータスコード403でクライアントに返却する。 * - | (7) - | \ ``CsrfTokenRepository``\ の実装としてHTTPセッションにCSRFトークンを保存する、\ ``org.springframework.security.web.csrf.HttpSessionCsrfTokenRepository``\ クラスを定義する。 * - | (8) - | \ ``SessionAuthenticationStrategy``\ の実装として、複数の\ ``SessionAuthenticationStrategy``\ を使用できる\ ``org.springframework.security.web.authentication.session.CompositeSessionAuthenticationStrategy``\ * - | (9) - | \ ``CompositeSessionAuthenticationStrategy``\ に、\ ``org.springframework.security.web.csrf.CsrfAuthenticationStrategy``\ を追加する。 * - | (10) - | \ ``CompositeSessionAuthenticationStrategy``\ コンストラクタの第1引数で、\ ``CsrfTokenRepository``\ を参照する。 .. note:: **AccessDeniedHandlerImplのerrorPageプロパティを省略した場合の、エラーハンドリングについて** web.xmlに、以下の設定を行うことで、任意のページに遷移させることができる。 **web.xml** .. code-block:: xml 403 /WEB-INF/views/common/error/csrf-error.jsp .. tabularcolumns:: |p{0.10\linewidth}|p{0.90\linewidth}| .. list-table:: :header-rows: 1 :widths: 10 90 * - 項番 - 説明 * - | (1) - | error-code要素に、ステータスコード403を設定する。 * - | (2) - | location要素に、遷移先のパスを設定する。 .. note:: **ステータスコード403以外を返却したい場合** リクエストに含まれるCSRFトークンが一致しない場合、ステータスコード403以外を返却したい場合は、\ ``org.springframework.security.web.access.AccessDeniedHandler``\ インタフェースを 実装した、独自のAccessDeniedHandlerを作成する必要がある。 詳細は、\ `Spring Securityのレファレンスドキュメント `_\ を参照されたい。 .. todo:: **Spring Security のバージョンが、 3.2.0 以降の場合の設定** Spring Security 3.2を使用する場合、\ ````\ 要素に\ ````\ 要素を設定することで、 前述した設定を省略することができる。 \ `Spring Securityのレファレンスドキュメント `_\ を参照されたい。 .. _csrf_spring-mvc-setting: spring-mvc.xmlの設定 """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" CSRFトークン用の\ ``RequestDataValueProcessor``\ 実装クラスを利用し、Springのタグライブラリの\ ````\ タグを使うことで、自動的にCSRFトークンを、hiddenに埋め込むことができる。 .. code-block:: xml :emphasize-lines: 5-7 .. 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定義を設定する。 | factory-methodに、createメソッドを指定する。 .. note:: CSRFトークンの生成、チェックは、\ ``CsrfFilter``\ が行うため、Controllerでは特に、CSRF対策は意識しなくてよい。 フォームによる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トークンはログインのタイミングで生成される。 .. warning:: \ ``CsrfRequestDataValueProcessor``\ を設定している状態で \ ``...``\ とした場合(GETメソッドを指定してフォームを送信した場合)、 * ブラウザのアドレスバーにCSRFトークンが表示される * ブックマークした場合、ブックマークにCSRFトークンが記録される * WebサーバのアクセスログにCSRFトークンが記録される ため、攻撃者にCSRFトークンを悪用されるリスクが高くなる。 この事象を回避するためには、 .. code-block:: jsp ... と書く代わりに、 .. 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. と説明されており、必須ではないが対応することが推奨される。 Spring Securityのデフォルト実装では、CSRFトークンの値としてランダムなUUIDを生成しているため、 仮にCSRFトークンが漏洩してもセッションハイジャックされる事はないという点を補足しておく。 また、Spring 4を使用すると、この問題は解消される。(\ ````\ を使用してもCSRFトークンはURLに現れない)。 .. _csrf_formtag-use: CSRFトークンを明示的に埋め込む方法 """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" \ ````\ タグを使用しない場合は、明示的に、\ ````\ タグを追加する必要がある。 \ ``CsrfFilter``\ により、\ ``org.springframework.security.web.csrf.CsrfToken``\ オブジェクトが、リクエストスコープの \ ``_csrf``\ 属性に設定されるため、jspでは、以下のように設定すればよい .. 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.parameterName``\ でリクエストパラメータ名を、\ ``_csrf.token``\ で、CSRFトークンを設定する。 * - | (2) - | Spring Securityのデフォルト実装では、\ ``name``\ 属性に\ ``_csrf``\ が設定されている \ ````\ タグが追加され、CSRFトークンが埋め込まれる。 .. note:: CSRFトークンチェック対象のリクエスト(デフォルトでは、HTTPメソッドが、GET, HEAD, TRACE, OPTIONS以外の場合)で、CSRFトークンがない、または サーバー上に保存されているトークン値と、送信されたトークン値が異なる場合は、\ ``AccessDeniedHandler``\ によりアクセス拒否処理が行われる。 デフォルトでは403エラーとなり、\ ``AccessDeniedHandlerImpl``\ の\ ``errorPage``\ プロパティで指定したエラーページに遷移する。 詳細は、\ :ref:`spring-security.xmlの設定 `\ を参照されたい。 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) - | \ ````\ タグに、\ ``${f:h(_csrf.token)}``\ で取得したCSRFトークンを設定する。 * - | (2) - | \ ````\ タグに、\ ``${f:h(_csrf.headerName)}``\ で取得したヘッダ名を設定する。 * - | (3) - | \ ````\ タグに、設定したCSRFトークンを取得する。 * - | (4) - | \ ````\ タグに、設定したCSRFヘッダ名を取得する。 * - | (5) - | リクエストヘッダーに、\ ````\ タグで設定したヘッダ名(デフォルト:X-CSRF-TOKEN)、CSRFトークンの値を設定する。 * - | (6) - | この書き方はXSSの可能性があるので、実際にJavaScriptコードを書くときは気を付けること。 | 今回の例では\ ``data.addResult``\ 、\ ``data.subtractResult``\ 、\ ``data.multipyResult``\ 、\ ``data.divideResult``\ の全てが数値型であるため、問題ない。 JSONでリクエストを送信する場合も、同様にHTTPヘッダを設定すればよい。 .. todo:: \ :doc:`../ArchitectureInDetail/Ajax`\ 対応する例がなくなっているため、例を直す。 マルチパートリクエスト(ファイルアップロード)時の留意点 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | 一般的に、ファイルアップロードなどマルチパートリクエストを送る場合、formから送信される値を\ ``Filter``\ では取得できない。 | そのため、これまでの説明だけでは、マルチパートリクエスト時に\ ``CsrfFileter``\ がCSRFトークンを取得できず、不正なリクエストと見なされてしまう。 そのため、以下のどちらかの方法によって、対策する必要がある。 * \ ``org.springframework.web.multipart.support.MultipartFilter``\ を使用する * クエリのパラメータでCSRFトークンを送信する .. note:: それぞれメリット・デメリットが存在するため、システム要件を考慮して、採用する対策方法を決めて頂きたい。 ファイルアップロードの詳細については、\ :doc:`FileUpload <../ArchitectureInDetail/FileUpload>`\ を参照されたい。 .. _csrf_use-multipart-filter: MultipartFilterを使用する方法 """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" | 通常、マルチパートリクエストの場合、formから送信された値は\ ``Filter``\ 内で取得できない。 | \ ``org.springframework.web.multipart.support.MultipartFilter``\ を使用することで、マルチパートリクエストでも、\ ``Filter``\ 内で、 | formから送信された値を取得することができる。 .. warning:: \ ``MultipartFilter``\ を使用した場合、\ ``springSecurityFilterChain``\による認証・認可処理が行われる前にアップロード処理が行われるため、 認証又は認可されていないユーザーからのアップロード(一時ファイル作成)を許容してしまう。 \ ``MultipartFilter``\ を使用するには、以下のように設定すればよい。 **web.xmlの設定例** .. code-block:: xml MultipartFilter org.springframework.web.multipart.support.MultipartFilter springSecurityFilterChain org.springframework.web.filter.DelegatingFilterProxy MultipartFilter /* springSecurityFilterChain /* .. tabularcolumns:: |p{0.10\linewidth}|p{0.90\linewidth}| .. list-table:: :header-rows: 1 :widths: 10 90 * - 項番 - 説明 * - | (1) - | \ ``org.springframework.web.multipart.support.MultipartFilter``\ を 定義する。 * - | (2) - | \ ``springSecurityFilterChain``\ より前に、\ ``MultipartFilter``\ を定義すること。 **JSPの実装例** .. code-block:: jsp
.. tabularcolumns:: |p{0.10\linewidth}|p{0.90\linewidth}| .. list-table:: :header-rows: 1 :widths: 10 90 * - 項番 - 説明 * - | (1) - | spring-mvc.xmlの設定の通り、\ ``CsrfRequestDataValueProcessor``\ が定義されている場合、 | \ ````\ タグを使うことで、CSRFトークンが埋め込まれた\ ````\ タグが自動的に追加されるため、 | JSPの実装で、CSRFトークンを意識する必要はない。 | | **
タグを使用する場合** | \ :ref:`csrf_formtag-use`\ でCSRFトークンを設定すること。 クエリパラメータでCSRFトークンを送る方法 """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" 認証又は認可されていないユーザーからのアップロード(一時ファイル作成)を防ぎたい場合は、 \ ``MultipartFilter``\ は使用せず、クエリパラメータでCSRFトークンを送る必要がある。 .. warning:: この方法でCSRFトークンを送った場合、\ ``...``\ 使用時と同様に、CSRFトークンがURLに現れるという問題がある。 以下に、CSRFトークンをクエリパラメータとして送る実装例を示す。 **JSPの実装例** .. code-block:: jsp
.. tabularcolumns:: |p{0.10\linewidth}|p{0.90\linewidth}| .. list-table:: :header-rows: 1 :widths: 10 90 * - 項番 - 説明 * - | (1) - | \ ````\ タグのaction属性に、以下のクエリを付与する必要がある。 | \ ``?${f:h(_csrf.parameterName)}=${f:h(_csrf.token)}``\ | \ ````\ タグを使用する場合も、同様の設定が必要である。 .. raw:: latex \newpage