Spring Boot + Spring Securityで、特定のURLのみCSRF対策トークンチェックを行いたい

今回はSpring Boot + Spring Security環境で、特定のURLのみCSRF対策トークンチェックを行う、または特定のURLのみチェックを行わないように設定する方法を記載します。

そもそも

Spring Securityを入れると自動でCSRFチェックが有効になります。これは大変有難いことなのですが、画面がなくURLを呼び出してもらうだけのAPI(RESTなAPI?)を作成する場合、これがちょっと邪魔になります。

なにせ前画面がないので、わざわざCSRFトークンを取るリクエストを出して、取得できたトークンを、本命のリクエストの”X-CSRF-TOKEN”というヘッダに加えて送信しないといけないのです。確かにセキュリティ上チェックする方が良いのでしょう。2リクエスト出してヘッダーを書き換えないといけないので、簡単なCSRFは防げます。

しかし、作成したいAPIがログイン情報に何も関与しない、誰でも使用可能な機能ならどうでしょうか。もしくは別でアカウント情報をチェックするとか。その場合は特定のURLのみCSRFチェックをOFFにしたい。

それではと、調査したところ…

調べたら出てきました。きっとマニュアルにも記載されているのでしょう。しかし、個人のブログになります。なぜなら見つけられなかったからです!インターネット上の諸先輩方にはいつも感謝しております!!いくらしても感謝し足りません!!!やつあたりです!!!!

この記事が初めに見つかりましたが、バージョンが違うのか自分の環境では構文エラーになりました(動作がおかしかったのかな?忘れました)。

Spring Boot: enable the CSRF check selectively only for some requests
http://blog.netgloo.com/2014/09/28/spring-boot-enable-the-csrf-check-selectively-only-for-some-requests/

次にこの記事を見つけ、上と合体させて、無事に実現できました。

csrf – scpting security requireCsrfProtectionMatcher with csrfTokenRepository – Stack Overflow
http://stackoverflow.com/questions/31098323/scpting-security-requirecsrfprotectionmatcher-with-csrftokenrepository

実装方法の解説

実装方法は、とにかくこうです。すごいシンプルで簡単です。

  • ①Spring SecurityのSecurityConfigクラス(@EnableWebMvcSecurity)をいらいます。
  • ②CSRF対策のトークンチェックが必要なURL、不要なURLを振り分けるRequestMatcherクラスを用意します。
  • ③CSRFの設定に”requireCsrfProtectionMatcher”というメソッドがあるので、こいつに②を設定します。

これだけです。簡単で、影響規模が小さいので解説範囲が狭く済み、かつ、もしかすると必要な人もでてくるかも知れない内容だったので、記事にしたのです。ありがたい限りです。

ソース

アクセスURLで/api/以下のリクエストではトークンチェックを行わないようにしています。実際には自前の、別の認証に通らないとAPIは使用できないようになっています。どっちが面倒かは考えものですが、今回はクライアント側の楽さを優先した格好でしょうか。

/**
 * Spring Securityの設定クラス
 * @author fujita
 *
 */
@Configuration
@EnableWebMvcSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    
    /**
     * HttpSecurity向け設定
     */
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        
        // 他の設定がここらにあるはず
        
        /**
         * CSRF適用URL判断クラス
         */
        RequestMatcher csrfRequestMatcher = new RequestMatcher() {

            private AntPathRequestMatcher disabledRequestMatcher =
                    new AntPathRequestMatcher("/api/**");

            @Override
            public boolean matches(HttpServletRequest request) {

                // GETならCSRFのチェックはしない
                if("GET".equals(request.getMethod()))
                    return false;
                
                // 特定のURLに該当する場合、CSRFチェックしない
                if(disabledRequestMatcher.matches(request))
                    return false;
                
                return true;
            }

        };
        http.csrf().requireCsrfProtectionMatcher(csrfRequestMatcher);
    }
}

完全にOFFにする実装以外にも、とりあえずAPIを実装したい場合にも、一時的にOFFにして検証するときに知っていて損はないはずです。あ、その場合は、全体的にOFFにすれば良いだけでした、忘れてください。

ともかく以上です。誰かの役に立ってほしいものです。