Spring Security 使い方メモ　CSRF


:::note warn
Spring Security は 5.4 以降、設定の書き方に大幅な変更が入っています。
詳しくは @suke_masa さんの [Spring Security 5.7でセキュリティ設定の書き方が大幅に変わる件 - Qiita](https://qiita.com/suke_masa/items/908805dd45df08ba28d8) を参照してください。
:::


[基礎・仕組み的な話](http://qiita.com/opengl-8080/items/c105152c9ca48509bd0c)
[認証・認可の話](http://qiita.com/opengl-8080/items/032ed0fa27a239bdc1cc)
[Remember-Me の話](http://qiita.com/opengl-8080/items/7c34053c74448d39e8f5)
[セッション管理の話](http://qiita.com/opengl-8080/items/ad9159910501d1989876)
[レスポンスヘッダーの話](http://qiita.com/opengl-8080/items/4e800eb6990653b0395c)
[メソッドセキュリティの話](http://qiita.com/opengl-8080/items/76715fd3a92b6ca680d0)
[CORS の話](http://qiita.com/opengl-8080/items/db0aa1ec0be36886953b)
[Run-As の話](http://qiita.com/opengl-8080/items/49aa6381c16c8b9a7f3c)
[ACL の話](http://qiita.com/opengl-8080/items/24a3118ef36bcf55ba71)
[テストの話](http://qiita.com/opengl-8080/items/eaa8f4eb9286a3df7986)
[MVC, Boot との連携の話](http://qiita.com/opengl-8080/items/4824d4a8c012f7ec53db)

番外編
[Spring Security にできること・できないこと](http://qiita.com/opengl-8080/items/6dc37f8b77abb5ae1642)

CSRF とは Cross-site Request Forgery の略。
Forgery は「偽造」という意味らしい。

この攻撃が何なのかとか、対策方法については [IPA のサイト](https://www.ipa.go.jp/security/awareness/vendor/programmingv2/contents/301.html) とか [Wikipedia](https://ja.wikipedia.org/wiki/%E3%82%AF%E3%83%AD%E3%82%B9%E3%82%B5%E3%82%A4%E3%83%88%E3%83%AA%E3%82%AF%E3%82%A8%E3%82%B9%E3%83%88%E3%83%95%E3%82%A9%E3%83%BC%E3%82%B8%E3%82%A7%E3%83%AA) とかを参照のこと。

# Spring Security での CSRF 対策
Spring Security で namespace や Java Configuration を使用した場合は、デフォルトで CSRF 対策が有効になる。

CSRF 対策が有効になった場合、 `GET`, `HEAD`, `TRACE`, `OPTIONS` **<font color="red">以外</font>** の HTTP メソッド（`POST`, `PUT`, `DELETE`, `PATCH` など）でリクエストが来た場合にトークンのチェックが行われるようになる。

対象外となっているメソッドは、[HTTP の仕様ではサーバーの状態を変更すべきでない](https://triple-underscore.github.io/RFC7231-ja.html#section-4.2.1)ということになっている。
なので、ちゃんと HTTP の仕様に沿って Web サービスを作っていれば、 `GET` や `HEAD` などを CSRF の保護対象にする必要はないことになる（必要があるとすれば、それは Web サービスのほうがなんか変）。

# トークンの連携方法
デフォルトだと、トークンは `_csrf` という名前でリクエストスコープに保存されている。

このトークンは `CsrfToken` オブジェクトで、次のようなメソッドを持つ。

```java:CsrfToken.java
package org.springframework.security.web.csrf;

import java.io.Serializable;

public interface CsrfToken extends Serializable {
	String getHeaderName();
	String getParameterName();
	String getToken();
}
```

`getParameterName()` でリクエストパラメータで渡すときのパラメータ名を取得でき、 `getToken()` で実際のトークンの値が取得できる。

よって、 EL式などを使えば次のようにしてトークンをパラメータとして埋め込むことができるようになる。

```jsp:index.jsp
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@page contentType="text/html" pageEncoding="UTF-8"%>
<!doctype html>
<html>
    <head>
        <meta charset="utf-8" />
        <title>Hello Spring Security!!</title>
    </head>
    <body>
        <h1>Hello Spring Security!!</h1>
        
        <c:url var="logoutUrl" value="/logout" />
        <form action="${logoutUrl}" method="post">
            <input type="submit" value="logout" />
            <input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}" /> ★ここでトークンを埋め込んでいる
        </form>
    </body>
</html>
```

## HTTP ヘッダーでトークンを送信する
Ajax などでボディは JSON にする場合、上記のような方法でトークンを渡すことができない。

その場合は HTTP ヘッダーでトークンを渡す。

ヘッダー名は `${_csrf.headerName}` で取得できる。
あとは、ヘッダー名とトークンの値を HTTP 内に埋め込んで置き、 JavaScript で取り出して Ajax リクエストのヘッダーにセットすればいい。


# トークンの値
トークンの値は、 `UUID.randomUUID()` で生成されている。

最初これを見たとき「あれ、 `SecureRandom` を使わなくてええんか？」と思った。
しかし、 `UUID.randomUUID()` の Javadoc を見てみると次のように説明されている。

> このUUIDは、暗号強度の高い擬似乱数ジェネレータを使って生成されます。

https://docs.oracle.com/javase/jp/8/docs/api/java/util/UUID.html#randomUUID--

実際ソースを見てみると、 `SecureRandom` を使っている。
なので、別に `UUID.randomUUID()` でも問題ないっぽい。

# トークンが生成されるタイミング
[前述の IPA の解説ページ](https://www.ipa.go.jp/security/awareness/vendor/programmingv2/contents/301.html) などを見てみると、トークンを生成するタイミングがいくつか紹介されている。

> 照合情報には、他者が推測困難な値を用いる。そのような値として下記の 3つが挙げられる。
> ・セッションIDそのもの もしくは セッションIDから導かれるハッシュ値 等
> ・セッション開始時に一度生成され各ページで使われる、セッションIDとは無関係の値
> ・ページごとに生成される、毎回異なる値（ページトークン）

自分は最初、トークンは３つ目の「ページごと」に生成するものだと思っていた。
しかし、実際に Spring Security の CSRF 対策を動かしてみると、トークンは一度ログインしたあとはログアウトするまで同じ値が使いまわされていることに気付いた。

つまり、 Spring Security は２つ目の方法を採用していることになる。

調べてみると、ページごとにトークンを生成するのは、使いやすさの面で問題になるらしい。
[こちらの stackoverflow](http://stackoverflow.com/questions/16322067/csrf-generate-token-for-every-request) によると、ユーザーがブラウザの「戻るボタン」を使ったときを考慮しだすと話が複雑になるらしい。

ブラウザの「戻るボタン」を使って前の画面に戻ると、クライアント側のページに埋め込まれているトークンが古い状態になってしまう。
そのため次にアクションを起こすと、アプリケーションは CSRF 攻撃が行われたと誤認してしまうことになる。

ページごとにトークンを生成するとなると、トークンの管理が大変になるっぽい。

# エラーハンドリング
不正なトークンが渡ってきた場合、デフォルトだとサーバーのエラーページが表示される。

![spring-security.jpg](https://qiita-image-store.s3.amazonaws.com/0/28302/79e6a23c-470f-dc26-7d20-079e6746c4ab.jpeg)

このままだといろいろアレなので、エラーハンドリングを実装する。

## エラーページの指定
CSRF トークンのチェックは `CsrfFilter` によって行われる。
そして、もしトークンに問題がある場合は `AccessDeniedHandlerImpl` にエラー処理が委譲される。

`AccessDeniedHandlerImpl` には `errorPage` というプロパティがあり、エラー時のフォワード先を指定できる。

```html:access-denied.html
<!doctype html>
<html>
    <head>
        <meta charset="utf-8" />
        <title>不正リクエスト</title>
    </head>
    <body>
        <h1>不正なリクエストを検知しました</h1>
    </body>
</html>
```

**namespace**

```xml:applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:sec="http://www.springframework.org/schema/security"
       ...>
    
    <sec:http>
        ...
        <sec:access-denied-handler error-page="/access-denied.html" />
    </sec:http>

    ...
</beans>
```

- `<access-denied-handler>` タグを追加し、 `error-page` 属性で指定する。

**Java Configuration**

```java:MySpringSecurityConfig.java
...

@EnableWebSecurity
public class MySpringSecurityConfig extends WebSecurityConfigurerAdapter {
    
    @Override
    public void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                ...
                .and()
                .exceptionHandling()
                .accessDeniedPage("/access-denied.html");
    }

    ...
}
```

- `exceptionHandling().accessDeniedPage(String)` で指定する

![spring-security.jpg](https://qiita-image-store.s3.amazonaws.com/0/28302/a0f5c180-47ce-0f93-2b0e-3fabfd4aa512.jpeg)

CSRF トークンを不正な値にしてエラーを発生させると、指定されたページにフォワードされた。

※`AccessDeniedHandlerImpl` は[通常の権限チェックのエラーハンドリングでも利用される](http://qiita.com/opengl-8080/items/032ed0fa27a239bdc1cc#%E8%AA%8D%E5%8F%AF%E3%82%A8%E3%83%A9%E3%83%BC%E6%99%82%E3%81%AE%E5%88%B6%E5%BE%A1)ので、どちらのエラーでも不自然じゃないメッセージにしておかないといけない点に注意

どうしても２つのエラーハンドリングの実装クラスを分けたい場合は、 `CsrfFilter` と `ExceptionTranslationFilter` のそれぞれの `AccessDeniedHandler` を直接上書きするように [BeanPostProcessor](https://www.tutorialspoint.com/spring/spring_bean_post_processors.htm) を実装するみたいなことをすればいいと思う。

## <span id="access-denied-handler">実装を指定する</span>
```java:MyAccessDeniedHandler.java
package sample.spring.security.handler;

import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.web.access.AccessDeniedHandler;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;

public class MyAccessDeniedHandler implements AccessDeniedHandler {
    
    @Override
    public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException, ServletException {
        if (response.isCommitted()) {
            return;
        }
        
        response.setStatus(HttpServletResponse.SC_FORBIDDEN);
        try (PrintWriter writer = response.getWriter()) {
            writer.println("Access Denied!!");
        }
    }
}
```

- `AccessDeniedHandler` を実装したクラスを用意する
- `handle()` メソッドの第三引数には発生した例外オブジェクトが渡される
    - CSRF トークンのエラーの場合は `MissingCsrfTokenException` か `InvalidCsrfTokenException` になる
    - `MissingCsrfTokenException` はサーバーで保存しているはずのトークンが見つからなかった場合に渡ってくる（主にセッション切れ）
    - `InvalidCsrfTokenException` はクライアントから来たトークンとサーバーが保存しているトークンに差異があった場合に渡ってくる
    - どちらのクラスも `CsrfException` を継承している

**namespace**

```xml:applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:sec="http://www.springframework.org/schema/security"
       ...>
    
    <bean id="accessDeniedHandler" class="sample.spring.security.handler.MyAccessDeniedHandler" />
    
    <sec:http>
        ...
        <sec:access-denied-handler ref="accessDeniedHandler" />
    </sec:http>

    ...
</beans>
```

- `<access-denied-handler>` タグの `ref` 属性で Bean を指定する

**Java Configuration**

```java:MySpringSecurityConfig.java
...

import sample.spring.security.handler.MyAccessDeniedHandler;

@EnableWebSecurity
public class MySpringSecurityConfig extends WebSecurityConfigurerAdapter {
    
    @Override
    public void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                ...
                .and()
                .exceptionHandling()
                .accessDeniedHandler(new MyAccessDeniedHandler());
    }

    ...
}
```

- `accessDeniedHandler(AccessDeniedHandler)` で指定する

![spring-security.jpg](https://qiita-image-store.s3.amazonaws.com/0/28302/314038ef-732e-d948-6c26-172f74be1b15.jpeg)

`MyAccessDeniedHandler` によって生成したレスポンスが出力された。



# セッションタイムアウト問題
デフォルトだと、 CSRF 対策用のトークンはセッションに保存される。
よって、セッションタイムアウトが発生するとトークンが取得できずに、 CSRF のトークンチェックのエラーが発生する。

![spring-security.jpg](https://qiita-image-store.s3.amazonaws.com/0/28302/b79faf7e-fe1d-065a-3c1e-c263b5be0149.jpeg)

この場合は、セッション切れなので単純にログインページに遷移させたい。
しかし、 `AccessDeniedHanlderImpl` の `errorPage` を指定する方法だとセッション切れと不正トークンを区別できないので、両者を区別してハンドリングしたい場合は必然的に `AccessDeniedHandler` を自作することになりそう。

```java:MyAccessDeniedHandlerImpl.java
package sample.spring.security.handler;

import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.web.DefaultRedirectStrategy;
import org.springframework.security.web.access.AccessDeniedHandler;
import org.springframework.security.web.csrf.MissingCsrfTokenException;

import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

public class MyAccessDeniedHandler implements AccessDeniedHandler {
    
    @Override
    public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException, ServletException {
        if (response.isCommitted()) {
            return;
        }

        if (accessDeniedException instanceof MissingCsrfTokenException) {
            DefaultRedirectStrategy redirectStrategy = new DefaultRedirectStrategy();
            redirectStrategy.sendRedirect(request, response, "/login");
        } else {
            RequestDispatcher dispatcher = request.getRequestDispatcher("/access-denied.html");
            dispatcher.forward(request, response);
        }
    }
}
```

例外が `MissingCsrfTokenException` の場合だけログインページにリダイレクトさせて、それ以外は専用のエラーページに遷移させる。

`MyAccessDeniedHandler` の設定方法は[エラーの説明のところを参照](#access-denied-handler)。

**不正なトークンを受け取った場合**

![spring-security.jpg](https://qiita-image-store.s3.amazonaws.com/0/28302/007e3683-1976-9906-f4c4-5ee04a3fe257.jpeg)

**セッション切れの場合**

![spring-security.jpg](https://qiita-image-store.s3.amazonaws.com/0/28302/d739fd22-eb19-afe5-77cd-4840e4cc96c6.jpeg)


## ログインのときもセッションタイムアウトの可能性がある
もしログインのときに CSRF のチェックをしていない場合、知らない間に攻撃者が用意したアカウントでログインさせられるかもしれない。
そして、そうと気付かずログイン後に個人情報に関わる入力をしたとする。
攻撃者は後で同じアカウントでログインし、被害者が知らずに入力した個人情報を履歴の照会機能などを利用して参照できてしまうかもしれない。

といった攻撃があるかもしれないので、 CSRF のチェックはログインのときにも行ったほうが良いとされているらしい。

しかし、前述したとおりデフォルトだと CSRF トークンはセッションに保存される。
そのため、ログイン画面を開いた後にセッションが切れるまで放置してからログインを行うと、 CSRF トークンがセッションから取得できずエラーになってしまう。

この場合、ログイン画面に戻したりエラー画面に遷移させるというのはちょっと使い勝手が悪い。

Spring Security のリファレンスでは、「Submit 前に JavaScript でトークンを取ってくるのが一般的だよ！」みたいなことが書かれている。

> A common technique to protect the log in form is by using a JavaScript function to obtain a valid CSRF token before the form submission.
> （訳）
> ログインフォームを守る一般的なテクニックは、フォームを Submit する前に JavaScript の関数で有効な CSRF トークンを取得するという方法です。

[18.5.2 Logging In](https://docs.spring.io/spring-security/site/docs/current/reference/htmlsingle/#csrf-login)

しかし、 JavaScript 側の具体的なソースが記載されていない。

仕方ないので想像で書いてみた。

```text:フォルダ構成
`-src/main/
  |-java/
  |  `-sample/spring/security/servlet/
  |    `-CsrfTokenServlet.java
  |
  `-webapp/
    |-js/
    |  |-jquery.min.js
    |  `-login.js
    |
    |-WEB-INF/
    |  `-applicationContext.xml
    |
    `-login.jsp
```

```xml:applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:sec="http://www.springframework.org/schema/security"
       ...>
    
    ...
    
    <sec:http>
        <sec:intercept-url pattern="/login.jsp" access="permitAll" />
        <sec:intercept-url pattern="/js/**" access="permitAll" />
        <sec:intercept-url pattern="/api/csrf-token" access="permitAll" />
        <sec:intercept-url pattern="/**" access="isAuthenticated()" />
        <sec:form-login login-page="/login.jsp" />
        ...
    </sec:http>
    
    ...
</beans>
```

- 未ログインの状態でもアクセスできるように、 `/js/**` や `/api/csrf-token` (CSRF トークンを取得するためのエンドポイント)を `permitAll` で定義している。
- また、ログイン画面を自作のもの（`login.jsp`）に置き換えるので `<form-login>` の `login-page` 属性で指定している。

```jsp:login.jsp
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@page contentType="text/html" pageEncoding="UTF-8"%>
<!doctype html>
<html>
    <head>
        <meta charset="utf-8" />
        <title>Login</title>
    </head>
    <body>
        <h1>Login</h1>
        
        <c:url var="loginUrl" value="/login" />
        <form id="form" action="${loginUrl}" method="post">
            <div>
                Username : <input type="text" name="username" />
            </div>
            <div>
                Password : <input type="password" name="password" />
            </div>
            
            <input type="button" id="loginButton" value="Login" />
            
            <input type="hidden" id="csrfToken" name="${_csrf.parameterName}" />
        </form>
        
        <c:url var="jsDirUrl" value="/js" />
        <script src="${jsDirUrl}/jquery.min.js"></script>
        <script src="${jsDirUrl}/login.js"></script>
    </body>
</html>
```

- CSRF トークンをセットする hidden は `value` を空にしておき、 Submit するときに設定する。

```js:login.js
$(function() {
    $('#loginButton').on('click', function() {
        $.ajax({
            url: 'api/csrf-token',
            type: 'GET'
        })
        .done(function(token) {
            $('#csrfToken').val(token);
            $('#form').submit();
        });
    });
});
```

- `loginButton` がクリックされたら Ajax で CSRF トークンを取得し、 hidden に設定してからフォームを submit している。

```java:CsrfTokenServlet.java
package sample.spring.security.servlet;

import org.springframework.security.web.csrf.CsrfToken;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;

@WebServlet("/api/csrf-token")
public class CsrfTokenServlet extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        CsrfToken token = (CsrfToken) req.getAttribute(CsrfToken.class.getName());
        try (PrintWriter writer = resp.getWriter()) {
            writer.print(token.getToken());
        }
    }
}
```

- `CsrfToken` はリクエストスコープに保存されているので、それを取得して `getToken()` で生のトークン値を取得、レスポンスとして返している
- なお、リファレンスでは `CsrfTokenArgumentResolver` を使って Spring MVC で実装する例が[紹介されている](https://docs.spring.io/spring-security/site/docs/current/reference/htmlsingle/#mvc-csrf-resolver)。
    - 普通はこっちの実装になると思う。

これで、ログイン直前でトークンを取得するようになるのでセッションが切れた後でもスムーズにログインできるようになる。
実際に使う場合は実装の細かいところを修正する必要があるだろうが、基本的な流れはこれで合っているのだろうか？

## セッションを使わず Cookie を使う
そもそもトークンの情報をセッションに保存しているために、タイムアウトという現象が発生している。

Spring Security には CSRF 対策用トークンを Cookie に保存する方法も用意されている。
なので、こちらを利用すればセッションタイムアウトについて気にする必要はなくなる。

ではなぜデフォルトで Cookie を使っていないのかというと、セッションよりも若干セキュリティのレベルが下がるかららしい。

若干というのは、

- Flash を使った特殊な攻撃が実行されたときに、 Cookie を使っていると CSRF 対策が有効に働かなくなることがあった
- 仮にトークンが盗まれた場合でも、セッションを使っていれば最悪セッションを無効にすれば即座にトークンを無効にすることができる

というぐらい。

Flash を使った攻撃方法については [こちら](http://lists.webappsec.org/pipermail/websecurity_lists.webappsec.org/2011-February/007533.html) に解説が載っている。

結構前（2011年2月）の話だが、 Ruby on Rails とかもこの問題に対して[対策を講じたりしていた](http://weblog.rubyonrails.org/2011/2/8/csrf-protection-bypass-in-ruby-on-rails/)らしい。

2017年現在どうなっているのか調べようとしたが、調べ方がよくないのか明確な情報は見つけられなかった。
[Flash Player のリリースノート](https://helpx.adobe.com/jp/flash-player/flash-player-releasenotes.html) とかも見てみたが、 10.2 付近にそれっぽいアップデートは見当たらなかった。。。

ただ、 Spring Security のリファレンスには Cookie を使うことについて次のように説明している。

> As previously mentioned, this is not as secure as using a session, but in many cases can be good enough.
> （訳）
> 前述のとおり、これ（Cookie を使うこと）はセッションほど安全ではありません。しかし、多くの場合でそれは十分に良いものです。

https://docs.spring.io/spring-security/site/docs/current/reference/htmlsingle/#csrf-timeouts

さて、これをどう判断すべきか。。。

Cookie を使う場合は、次のように実装する。

**namespace**

```xml:applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:sec="http://www.springframework.org/schema/security"
       ...>
    
    ...
    
    <bean id="tokenRepository"
          class="org.springframework.security.web.csrf.CookieCsrfTokenRepository" /> ★
    
    <sec:http>
        ...
        <sec:csrf token-repository-ref="tokenRepository" /> ★
    </sec:http>
    
    ...
</beans>
```

`CookieCsrfTokenRepository` を Bean として定義し、 `<csrf>` タグの `token-repository-ref` 属性で指定する。

**Java Configuration**

```java:
package sample.spring.security;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.web.csrf.CookieCsrfTokenRepository;

import java.util.Collections;

@EnableWebSecurity
@ComponentScan
public class MySpringSecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    public void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .antMatchers("/login").permitAll()
                .anyRequest().authenticated()
                .and()
                .formLogin()
                .and()
                .csrf().csrfTokenRepository(new CookieCsrfTokenRepository()); ★
    }
    
    ...
}
```

`.csrf().csrfTokenRepository()` に `CookieCsrfTokenRepository` のインスタンスを渡す。

![spring-security.jpg](https://qiita-image-store.s3.amazonaws.com/0/28302/c3413083-04a3-89e9-4176-f9319cecc972.jpeg)

Cookie に `XSRF-TOKEN` が追加され、そこにトークンの値が保存されている。

ちなみに、デフォルトだと httpOnly が true になるので JavaScript からこの Cookie にアクセスすることはできないようになっている。
JavaScript のフレームワークとかを使っていてどうしても JavaScript から Cookie を触れないと困るような場合は、この設定を
 false にしてあげる必要がある（リファレンスでは AngularJS を例に挙げているっぽい）。

**namespace**

```xml:applicationContext.xml
    <bean id="tokenRepository"
          class="org.springframework.security.web.csrf.CookieCsrfTokenRepository">
        <property name="cookieHttpOnly" value="false" /> ★
    </bean>
```

`CookieCsrfTokenRepository` の `cookieHttpOnly` プロパティを `false` にする。

**Java Configuration**

```java:MySpringSecurityConfig.java
    @Override
    public void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                ...
                .csrf().csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse()); ★
    }
```

`CookieCsrfTokenRepository.withHttpOnlyFalse()` でインスタンスを生成する。

### secure 属性の指定
通信が HTTPS だと勝手に secure 属性が有効になる模様。
`CookieCsrfTokenRepository` の実装を見ると次のようになっている。

```java:CookieCsrfTokenRepository.java
	@Override
	public void saveToken(CsrfToken token, HttpServletRequest request,
			HttpServletResponse response) {
		...
		Cookie cookie = new Cookie(this.cookieName, tokenValue);
		cookie.setSecure(request.isSecure()); ★ここ
		...

	}
```

# 参考
- [Spring Security Reference](http://docs.spring.io/spring-security/site/docs/4.2.1.RELEASE/reference/htmlsingle/)
- [TERASOLUNA Server Framework for Java (5.x) Development Guideline — TERASOLUNA Server Framework for Java (5.x) Development Guideline 5.3.0.RELEASE documentation](http://terasolunaorg.github.io/guideline/5.3.0.RELEASE/ja/)
- [java - CSRF: Generate token for every request - Stack Overflow](http://stackoverflow.com/questions/16322067/csrf-generate-token-for-every-request)
- [httpOnlyなCookieとは？ - それマグで！](http://takuya-1st.hatenablog.jp/entry/2013/12/02/160641)