Edited at

"Spring Boot" + "EXT JS"を使用したRESTアプリにおけるCSRF対策方針と実装例

More than 1 year has passed since last update.

上記組み合わせでの情報が少なかったのでまとめました。


対策方針

CSRF(クロスサイトリクエスト偽造)対策としては、一般的なトークン発行方式としています。

サーバ→クライアントへのトークン情報の通知はSet-Cookieを利用するのが楽ですね。Spring Bootがやってくれるので。1

クライアント→サーバへのトークン送信はJavaScriptで独自に書いて、GETリクエスト以外2にCookie情報をhttpヘッダに書かせます。


  1. Spring Boot

    1.1. CSRFのトークンの保存先をCookieとする。

    1.2. JSからのアクセスを可能とするためにHttpOnlyとしない。


  2. Ext JS

    2.1. GETリクエストは対象外とする。

    2.1. ExtでのAjaxリクエスト送信時にCookie値からトークン情報を取得し、Httpヘッダに付与する。


実装例


Spring Boot

デフォルトでCSRF対策は有効ですが、トークンの保存先指定と、httpOnlyの無効化の定義をしています。


SecurityConfig.java


public class SecurityConfig extends WebSecurityConfigurerAdapter {

@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
.and()
...// 後略。適宜必要な定義を。
}
}



Ext JS

Ajaxで飛ばす全てのリクエストにトークン付与が必要なので、上位に呼ばれる箇所で定義をしています。

"beforerequest"として定義することで、Ajaxリクエスト発行前に処理が行われるようにして、個別に実装しなくて良いようにしています。


app.js

Ext.Ajax.on('beforerequest', function (conn, request, eOpts) {

// GETリクエスト以外の場合にCSRFトークンを付与する。
if (request.method === 'GET') {
return;
}
// CookieのXSRF-TOKEN項目値を、任意httpヘッダX-XSRF-TOKENとして付与する。
if (document.cookie) {
var cookies = document.cookie.split(";");
for (var i = 0; i < cookies.length; i++) {
var str = cookies[i].split("=");
if (str[0] == 'XSRF-TOKEN') {
conn.setDefaultHeaders({ 'X-XSRF-TOKEN': unescape(str[1]) });
break;
}
}
}
});





  1. (最初勘違いして)Cookieでトークン渡しても、セッションIDをCookieで持たせてる場合と同じでブラウザが勝手に送信してしまうので、CSRF対策として意味ないのでは?と思ったのですが、Spring BootでのCSRFトークンのチェックはCookie値を見ているのではなく、「formデータの_csrf」または「httpヘッダのX-XSRF-TOKEN」を見ているのがミソでした。トークンをCookieからformデータかhttpヘッダに置き換える必要がありますが、これをクロスオリジンなJSではできない(正規ドメインのCookieを見れない)ので対策となっているわけです。 



  2. CSRF攻撃の特性を考えるとGETリクエストでは検証する必要ないですね。Spring Bootではサーバサイドチェックをしていません。