web-dev-qa-db-ja.com

Spring Security 3.2マルチパートリクエストのCSRFサポート

ここ数年、アプリケーションでSpring Securityを使用しています。先週、Spring Securityをバージョン3.1.4から3.2.0にアップグレードしました。アップグレードはうまくいき、アップグレード後のエラーは見つかりませんでした。

Spring Security 3.2.0のドキュメントを調べていると、CSRF保護とセキュリティヘッダーに関する新たに追加された機能に出くわしました。 Spring Security 3.2.0のドキュメントの指示に従って、保護されたリソースのCSRF保護を有効にしました。通常のフォームでは正常に機能しますが、アプリケーションのマルチパートフォームでは機能しません。フォームの送信時に、CsrfFilterは、リクエストにCSRFトークンがないことを示すアクセス拒否エラーをスローします(DEBUGログで判断)。マルチパートフォームでCSRF保護を機能させるために、 Spring Securityのドキュメント で提案されている最初のオプションを使用してみました。 2番目の推奨オプションは、URLを介してCSRFトークンをリークし、セキュリティリスクをもたらすため、使用しないでください。

ドキュメントに基づく設定の関連部分は、Githubで Gist として利用できます。 Springバージョン4.0.0を使用しています。

次のバリエーションをすでに試したが成功しなかったことに注意してください。

  1. web.xmlMultipartFilterを宣言していません。
  2. web.xmlMultipartFilterにリゾルバーBean名を設定しない。
  3. webContext.xmlでデフォルトのリゾルバーBean名filterMultipartResolverを使用します。

UPDATE:ドキュメント化された動作が単一ページのサンプルアプリでも機能しないことを確認しました。文書化された動作が期待どおりに機能することを誰かが確認できますか?使用できる実用的なアプリケーションの例はありますか?

24
manish

この問題は、Spring Securityチームの協力を得て解決できました。 Gist を更新して、実際の構成を反映させました。すべてを期待どおりに機能させるには、以下の手順に従う必要がありました。


1。共通ステップ

@ holmis83による回答 の説明に従って、MultipartFilterweb.xmlに追加し、Spring Security構成の前に追加されるようにします。

<filter>
    <display-name>springMultipartFilter</display-name>
    <filter-name>springMultipartFilter</filter-name>
    <filter-class>org.springframework.web.multipart.support.MultipartFilter</filter-class>
</filter>
<filter-mapping>
    <filter-name>springMultipartFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

<filter>
    <display-name>springSecurityFilterChain</display-name>
    <filter-name>springSecurityFilterChain</filter-name>
    <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
    <filter-name>springSecurityFilterChain</filter-name>
    <url-pattern>/*</url-pattern>
    <dispatcher>ERROR</dispatcher>
    <dispatcher>FORWARD</dispatcher>
    <dispatcher>REQUEST</dispatcher>
</filter-mapping>

2.1。 Apache Commons Multipart Resolverの使用

filterMultipartResolverルートのSpringアプリケーションコンテキストという名前のApache Commons Multipart Resolver Beanがあることを確認します。私はこれを再度強調しますルートコンテキストでマルチパートリゾルバーが宣言されていることを確認してください(通常applicationContext.xmlと呼ばれます)。例えば、

web.xml

<context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>
        classpath*:springWebMultipartContext.xml
    </param-value>
</context-param>

springWebMultipartContext.xml

<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    <bean id="filterMultipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
        <property name="maxUploadSize" value="100000000" />
    </bean>
</beans>

web.xmlで構成されているMultipartFilterが他のBean名を取得しないため、BeanがfilterMultipartResolverと呼ばれていることを確認してください。このBeanの名前はmultipartResolverだったため、私の初期設定は機能していませんでした。 web.xmlinit-paramを使用してMultipartFilterにBean名を渡そうとしたが、それも機能しなかった。

2.2。 Tomcat Multipartサポートの使用

Tomcat 7.0+にはマルチパートサポートが組み込まれていますが、明示的に有効にする必要があります。次のようにグローバルTomcat context.xmlファイルを変更するか、ローカルcontext.xmlファイルをWARファイルに含めて、このサポートがアプリケーションに他の変更を加えなくても機能するようにします。

<Context allowCasualMultipartParsing="true">
    ...
</Context>

Apache Commons Multipart Resolverを使用してこれらの変更を行った後、これまでのところ、アプリケーションはTomcat、Jetty、およびWeblogicで動作しています。

47
manish

この問題に少し取り組んだ後、CSRFトークンをマルチパートコンテンツの一部として埋め込むのではなく、Spring Securityで定義されたリクエストヘッダーを使用するだけで、はるかに簡単な解決策を見つけました。

AJAXライブラリを使用してjspでファイルをアップロードするためのライブラリを使用してヘッダーを設定する簡単な方法を次に示します。

var uploader = new AjaxUpload({
        url: '/file/upload',
        name: 'uploadfile',
        multipart: true,
        customHeaders: { '${_csrf.headerName}': '${_csrf.token}' },
        ...
        onComplete: function(filename, response) {
            ...
        },
        onError: function( filename, type, status, response ) {
            ...
        }
});

次に、ヘッダー付きのマルチパートリクエストを送信します。

X-CSRF-TOKEN: abcdef01-2345-6789-abcd-ef0123456789

ヘッダーの<meta />タグに埋め込むという推奨事項も、送信時にリクエストを停止し、JavaScriptを介してヘッダーを追加して、送信を完了することで問題なく機能します。

<html>
<head>
    <meta name="_csrf" content="${_csrf.token}"/>
    <!-- default header name is X-CSRF-TOKEN -->
    <meta name="_csrf_header" content="${_csrf.headerName}"/>
    <!-- ... -->
</head>
<body>
    <!-- ... -->
    <script>
        var token = $("meta[name='_csrf']").attr("content");
        var header = $("meta[name='_csrf_header']").attr("content");
        // Do whatever with values
    </script>
</body>
</html>

詳細: Spring Security-CSRF for AJAX and JSON Requests

4
rwyland

この部分:

<filter-mapping>
    <filter-name>multipartFilter</filter-name>
    <servlet-name>/*</servlet-name>
</filter-mapping>

する必要があります:

<filter-mapping>
    <filter-name>multipartFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

これは、Spring Security 3.2.0ドキュメントのエラーです。バグ 報告されています で、次のバージョンで修正される予定です。

4
holmis83

ほとんどの回答はサーバー年前に回答されています。

必要なら

RestTemplateでCSRFトークンを渡す

このブログは非常に啓発的です https://cloudnative.tips/passing-csrf-tokens-with-resttemplate-736b336a6cf6

Spring Security 5.0.7.RELEASEで

https://docs.spring.io/spring-security/site/docs/current/reference/html/csrf.html#csrf-multipart

Multipart/form-dataでCSRF保護を使用するには、2つのオプションがあります。各オプションにはトレードオフがあります。

-Spring Securityの前にMultipartFilterを配置する
-アクションにCSRFトークンを含める

つまり、最初のオプションの方が安全で、後者の方が簡単です。

Spring Securityフィルターの前にMultipartFilterを指定すると、MultipartFilterを呼び出す権限がないため、誰でもサーバーに一時ファイルを配置できます。ただし、承認されたユーザーのみが、アプリケーションによって処理されるファイルを送信できます。一時ファイルのアップロードによる影響はほとんどのサーバーにほとんどないため、これは一般的に推奨される方法です。

Java構成でSpring Securityフィルターの前にMultipartFilterが指定されていることを確認するには、ユーザーはbeforeSpringSecurityFilterChainを以下のようにオーバーライドできます。

public class SecurityApplicationInitializer extends AbstractSecurityWebApplicationInitializer {

  @Override
  protected void beforeSpringSecurityFilterChain(ServletContext servletContext) {
      insertFilters(servletContext, new MultipartFilter());
  }
}

XML構成のSpring Securityフィルターの前にMultipartFilterを確実に指定するために、ユーザーはMultipartFilterの要素がweb.xml内のspringSecurityFilterChainの前に配置されるようにすることができます。次に例を示します。

<filter>
  <filter-name>MultipartFilter</filter-name>
  <filter-class>org.springframework.web.multipart.support.MultipartFilter</filter-class>
</filter>
<filter>
  <filter-name>springSecurityFilterChain</filter-name>
  <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
  <filter-name>MultipartFilter</filter-name>
  <url-pattern>/*</url-pattern>
</filter-mapping>
<filter-mapping>
  <filter-name>springSecurityFilterChain</filter-name>
  <url-pattern>/*</url-pattern>
</filter-mapping>

別のオプション

権限のないユーザーが一時ファイルをアップロードすることを許可しない場合は、Spring Securityフィルターの後にMultipartFilterを配置し、フォームのaction属性にクエリパラメーターとしてCSRFを含めることもできます。 jspの例を以下に示します。

<form action="./upload?${_csrf.parameterName}=${_csrf.token}" method="post" enctype="multipart/form-data">

このアプローチの欠点は、クエリパラメータがリークされる可能性があることです。より一般的には、機密データをボディまたはヘッダー内に配置して、漏洩しないようにすることをお勧めします。

2
Shihe Zhang