【Spring Security】Spring Security の csrf トークンの仕組み
Spring で開発した web アプリケーションの csrf 対策を行うときは Spring Security のトークン生成機能を利用することが多いと思います。
今回はこの csrf トークン生成の流れや仕組みを扱っていきます。
この記事で扱うこと
csrf トークン付与の方法
以下、すでに Spring Security をアプリケーションに導入済みという前提で話を進めます。
Spring Security を有効にしていればデフォルトで csrf トークンは生成されるようになっています。
必要なことは生成されているトークンを POST 時に送信されるようにリクエストに付与するだけです。
次にサンプルコードとして、csrf トークンが付与された POST リクエストを送信する form タグを Thymeleaf で書きます。
<form method="post" action="/csrf/passed"> <input type="hidden" th:name="${_csrf.parameterName}" th:value="${_csrf.token}"> <input type="submit" value="submit" /> </form>
また form タグならば次のように th:action
を使用するだけで付与することもできます。
<form th:action="@{/csrf/passed}" method="post"> <input type="submit" value="submit" /> </form>
どちらのコードも次のようにレンダリングされます。
_csrf.parameterName と _csrf.token
ここまでで csrf トークンの付与の方法はわかりましたが突然 _csrf.....
といった見覚えのない変数が出てきましたね。。
これは一体どこから現れたのでしょうか?
この _csrf
は HttpServletRequest の属性値に格納されているオブジェクトです。
よって controller クラスでもこれらの値を参照することができます。
@GetMapping public String index(HttpServletRequest request) { CsrfToken csrf = ((CsrfToken)request.getAttribute("_csrf")); System.out.println(csrf.getParameterName()); System.out.println(csrf.getToken()); return "index"; }
Thymeleaf でも同じく HttpServletRequest からこれらの値にアクセスしていたというわけです。
csrf トークンの裏側の仕組み
ここまでは view や controller クラスでの csrf トークンの扱いを見てきましたが、普段の開発で意識しないより裏側の部分を見ていきます。
Spring Security の csrf トークンの裏側での処理を追うにあたって、登場人物は主に次の二つです。
裏側の処理を追うなら CsrfFilter クラスを見ていけばいいですが、内部で CsrfTokenRepository クラスを参照しています。
具体的には CsrfFilter が行う処理は以下の二つです。
トークンを HttpServletRequest の属性値に格納
すでに触れた HttpServletRequest に csrf トークンを格納する処理です。
csrf トークンの値自体は CsrfTokenRepository から取得しています。
csrf トークンの照合
ここまでは csrf トークンを埋め込む部分に関する話でしたが、無事に埋め込んだトークンがちゃんとリクエストで送信されているかどうかの確認処理です。
この csrf トークンの照合は更新系の Http メソッドのみ(GET
、HEAD
、TRACE
、OPTIONS
以外)で適応されます。
リクエストで飛んできた csrf トークンと CsrfTokenRepository のトークンを比較し、同じであれば controller クラスに到達し通常通り view を返します。
異なれば例外をスローし、デフォルトではエラー画面が表示されます。
まとめ
簡単ながら csrf トークン付与の方法と裏側の処理の話を書きました。
csrf トークン周りに関してブラックボックスに感じている人がいれば参考になればと思います。