web-dev-qa-db-ja.com

Spring Security 3.2.0.RELEASEでは、純粋にHTMLでタグライブラリがないページでCSRFトークンを取得するにはどうすればよいですか

今日、Spring Security 3.1.4からJava config依存関係)をJava config。CSRFがオンになっている新しい3.2.0リリースにアップグレードしました。デフォルトで、「http.csrf()。disable()」を使用してオーバーライドされたconfigureメソッドで無効にできることを知っていますが、無効にしたくないと仮定しますが、JSPのないログインページにCSRFトークンが必要ですタグライブラリまたはSpringタグライブラリが使用されています。

ログインページは、Yeomanを使用して生成したBackboneアプリで使用する純粋なHTMLです。 「期待されるCSRFトークンが見つかりません。セッションが期限切れになりましたか?」というメッセージが表示されないように、HttpSessionに含まれるCSRFトークンをフォームまたはヘッダーとして含める方法を教えてください。例外?

23
Patrick Grimard

参照 で概説されているように、_csrfという名前の要求属性を使用してCSRFを取得できます。 CSRFをHTMLページに追加するには、JavaScriptを使用して、リクエストに含める必要のあるトークンを取得する必要があります。

本文のJSONは外部ドメインから取得できるため、JSONの本文よりもヘッダーとしてトークンを返す方が安全です。たとえば、JavaScriptは次によって処理されるURLを要求できます。

CsrfToken token = (CsrfToken) request.getAttribute("_csrf");
// Spring Security will allow the Token to be included in this header name
response.setHeader("X-CSRF-HEADER", token.getHeaderName());
// Spring Security will allow the token to be included in this parameter name
response.setHeader("X-CSRF-PARAM", token.getParameterName());
// this is the value of the token to be included as either a header or an HTTP parameter
response.setHeader("X-CSRF-TOKEN", token.getToken());

JavaScriptは、ヘッダー名またはパラメーター名とトークンを応答ヘッダーから取得し、ログインリクエストに追加します。

23
Rob Winch

@ rob-winchは正しいですが、セッションからトークンを取得することをお勧めします。 Spring-SecurityがSessionManagementFilterを使用してCsrfAuthenticationStrategyに新しいトークンを生成する場合、リクエストではなくSessionに設定します。したがって、間違ったcsrfトークンで終わる可能性があります。

public static final String DEFAULT_CSRF_TOKEN_ATTR_NAME = HttpSessionCsrfTokenRepository.class.getName().concat(".CSRF_TOKEN");
CsrfToken sessionToken = (CsrfToken) request.getSession().getAttribute(DEFAULT_CSRF_TOKEN_ATTR_NAME);
4
bsmk

注:私はCORSとAngularJSを使用しています。

注²:見つかった Stateless Spring Security Part 1 :ステートレスCSRF保護 これは、AngularJSがCSRFを処理する方法を維持するために興味深いものです。

Spring Security CSRF Filter を使用する代わりに(特に@Rob Winchのもの)、 The Login Page:Angular = JSとSpring SecurityパートII

これに加えて、Access-Control-Allow-Headers: ..., X-CSRF-TOKEN(CORSによる)を追加する必要がありました。

実際、このメソッドは、応答にヘッダーを追加するよりもわかりやすいです。

コードは次のとおりです。

HttpHeaderFilter.Java

@Component("httpHeaderFilter")
public class HttpHeaderFilter extends OncePerRequestFilter {
    @Autowired
    private List<HttpHeaderProvider> providerList;

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        providerList.forEach(e -> e.filter(request, response));

        if (HttpMethod.OPTIONS.toString().equals(request.getMethod())) {
            response.setStatus(HttpStatus.OK.value());
        }
        else {
            filterChain.doFilter(request, response);
        }
    }
}

HttpHeaderProvider.Java

public interface HttpHeaderProvider {
    void filter(HttpServletRequest request, HttpServletResponse response);
}

CsrfHttpHeaderProvider.Java

@Component
public class CsrfHttpHeaderProvider implements HttpHeaderProvider {
    @Override
    public void filter(HttpServletRequest request, HttpServletResponse response) {
        response.addHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_HEADERS, "X-CSRF-TOKEN");
    }
}

CsrfTokenFilter.Java

@Component("csrfTokenFilter")
public class CsrfTokenFilter extends OncePerRequestFilter {
    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        CsrfToken csrf = (CsrfToken)request.getAttribute(CsrfToken.class.getName());

        if (csrf != null) {
            Cookie cookie = WebUtils.getCookie(request, "XSRF-TOKEN");

            String token = csrf.getToken();

            if (cookie == null || token != null && !token.equals(cookie.getValue())) {
                cookie = new Cookie("XSRF-TOKEN", token);
                cookie.setPath("/");

                response.addCookie(cookie);
            }
        }

        filterChain.doFilter(request, response);
    }
}

web.xml

...
<filter>
    <filter-name>httpHeaderFilter</filter-name>
    <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
    <async-supported>true</async-supported>
</filter>
<filter-mapping>
    <filter-name>httpHeaderFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>
...

security-context.xml

...
<custom-filter ref="csrfTokenFilter" after="CSRF_FILTER"/>
...

app.js

...
.run(['$http', '$cookies', function ($http, $cookies) {
    $http.defaults.transformResponse.unshift(function (data, headers) {
        var csrfToken = $cookies['XSRF-TOKEN'];

        if (!!csrfToken) {
            $http.defaults.headers.common['X-CSRF-TOKEN'] = csrfToken;
        }

        return data;
    });
}]);
2