1. 概要

以前の記事では、CSRF攻撃がSpringMVCアプリケーションにどのように影響するかを説明しました。

この記事では、さまざまなケースを検討して、ステートレスREST APIがCSRF攻撃に対して脆弱である可能性があるかどうか、および脆弱である場合は、それらから保護する方法を決定します。

2. REST APIにはCSRF保護が必要ですか?

まず、専用ガイドでCSRF攻撃の例を見つけることができます。

このガイドを読むと、サーバー側で盗むセッションがないため、ステートレスRESTAPIはこの種の攻撃の影響を受けないと思うかもしれません。

典型的な例を見てみましょう:SpringRESTAPIアプリケーションとJavascriptクライアント。 クライアントは、セキュリティで保護されたトークン(JSESSIONIDや JWT など)を使用します。これは、ユーザーが正常にサインインした後にRESTAPIが発行します。

CSRFの脆弱性は、クライアントがこれらのクレデンシャルを保存してAPIに送信する方法によって異なります。

さまざまなオプションと、それらがアプリケーションの脆弱性にどのように影響するかを確認しましょう。

典型的な例として、SpringRESTAPIアプリケーションとJavascriptクライアントを取り上げます。 クライアントは、セキュリティで保護されたトークン(JSESSIONIDや JWT など)を使用します。これは、ユーザーが正常にサインインした後にRESTAPIが発行します。

2.1. クレデンシャルは永続化されません

REST APIからトークンを取得したら、そのトークンをJavaScriptグローバル変数として設定できます。 これにより、トークンがブラウザのメモリに保存され、現在のページでのみ使用できるようになります。

これが最も安全な方法です。CSRFおよびXSS攻撃は常に、新しいページでクライアントアプリケーションを開くことにつながり、サインインに使用された最初のページのメモリにアクセスできません。

ただし、ユーザーはページにアクセスまたは更新するたびに再度サインインする必要があります。

モバイルブラウザでは、システムがメモリをクリアするため、ブラウザがバックグラウンドになっても発生します。

これはユーザーを制限しているため、このオプションが実装されることはめったにありません

2.2. ブラウザストレージに保存されているクレデンシャル

トークンをブラウザストレージ(たとえば、セッションストレージ)に保持できます。 次に、JavaScriptクライアントはそこからトークンを読み取り、すべてのRESTリクエストでこのトークンを含む認証ヘッダーを送信できます。

これは、たとえばJWTを使用する一般的な方法です。実装が簡単で、攻撃者がCSRF攻撃を使用するのを防ぎます。 実際、Cookieとは異なり、ブラウザのストレージ変数はサーバーに自動的に送信されません。

ただし、この実装はXSS攻撃に対して脆弱です:悪意のあるJavaScriptコードがブラウザのストレージにアクセスし、リクエストとともにトークンを送信する可能性があります。 この場合、アプリケーションを保護する必要があります

2.3. Cookieに保存されている資格情報

もう1つのオプションは、Cookieを使用して資格情報を永続化することです。 次に、アプリケーションの脆弱性は、アプリケーションがCookieをどのように使用するかによって異なります。

Cookieを使用して、JWTのように資格情報のみを保持できますが、ユーザーを認証することはできません。

JavaScriptクライアントはトークンを読み取り、承認ヘッダーでAPIに送信する必要があります。

この場合、アプリケーションはCSRFに対して脆弱ではありません:Cookieが悪意のあるリクエストを介して自動的に送信された場合でも、RESTAPIはCookieからではなく認証ヘッダーから資格情報を読み取ります。 ただし、クライアントがCookieを読み取れるようにするには、HTTPのみのフラグをfalseに変更する必要があります。

ただし、これを行うことにより、アプリケーションは前のセクションのようにXSS攻撃に対して脆弱になります。

別のアプローチは、HTTP-onlyフラグをtrueに設定して、セッションCookieからの要求を認証することです。 これは通常、SpringSecurityがJSESSIONIDCookieで提供するものです。 もちろん、APIをステートレスに保つために、サーバー側でセッションを使用してはなりません。

この場合、アプリケーションはステートフルアプリケーションのようにCSRFに対して脆弱です:CookieはRESTリクエストとともに自動的に送信されるため、悪意のあるリンクをクリックすると認証された操作が実行される可能性があります。

2.4. その他のCSRFの脆弱な構成

一部の構成では、クレデンシャルとしてセキュアトークンを使用しませんが、CSRF攻撃に対して脆弱である可能性もあります。

これは、 HTTP基本認証 HTTPダイジェスト認証、およびmTLSの場合です。

これらはあまり一般的ではありませんが、同じ欠点があります。ブラウザは、HTTPリクエストで自動的にクレデンシャルを送信します。 このような場合、CSRF保護を有効にする必要があります。

3. SpringBootでCSRF保護を無効にする

Spring Securityは、バージョン4以降、デフォルトでCSRF保護を有効にします。

プロジェクトで必要ない場合は、カスタムWebSecurityConfigurerAdapterで無効にできます。

@Configuration
public class SpringBootSecurityConfiguration 
  extends WebSecurityConfigurerAdapter {
    @Override
    public void configure(HttpSecurity http) throws Exception {
        http.csrf().disable();
    }
}

4. RESTAPIでCSRF保護を有効にする

4.1. スプリング構成

プロジェクトでCSRF保護が必要な場合は、カスタムWebSecurityConfigurerAdapter でCookieCsrfTokenRepositoryを使用して、Cookieを含むCSRFトークンを送信できます。

JavaScriptクライアントから取得できるようにするには、HTTP-onlyフラグをfalseに設定する必要があります。

@Configuration
public class SpringSecurityConfiguration extends WebSecurityConfigurerAdapter {
    @Override
    public void configure(HttpSecurity http) throws Exception {
        http.csrf().csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse());
    }
}

アプリを再起動すると、リクエストはHTTPエラーを受け取ります。これは、CSRF保護が有効になっていることを意味します。

ログレベルをDEBUGに調整することで、これらのエラーがCsrfFilterクラスから発行されていることを確認できます。

<logger name="org.springframework.security.web.csrf" level="DEBUG" />

次のように表示されます。

Invalid CSRF token found for http://...

また、ブラウザに新しい XSRF-TOKENCookieが存在することを確認する必要があります。

RESTコントローラーに数行追加して、APIログにも情報を書き込みましょう。

CsrfToken token = (CsrfToken) request.getAttribute("_csrf");
LOGGER.info("{}={}", token.getHeaderName(), token.getToken());

4.2. クライアント構成

クライアント側のアプリケーションでは、 XSRF-TOKENCookieが最初のAPIアクセスの後に設定されます。 JavaScript正規表現を使用して取得できます。

const csrfToken = document.cookie.replace(/(?:(?:^|.*;\s*)XSRF-TOKEN\s*\=\s*([^;]*).*$)|^.*$/, '$1');

次に、APIの状態を変更するすべてのRESTリクエスト(POST、PUT、DELETE、およびPATCH)にトークンを送信する必要があります。

SpringはX-XSRF-TOKENヘッダーでそれを受け取ることを期待しています。 JavaScript FetchAPIを使用して簡単に設定できます。

fetch(url, {
    method: 'POST',
    body: JSON.stringify({ /* data to send */ }),
    headers: { 'X-XSRF-TOKEN': csrfToken },
})

これで、リクエストが機能していることがわかり、「無効なCSRFトークン」エラーがRESTAPIログに表示されなくなりました。

したがって、攻撃者がCSRF攻撃を実行することは不可能になります。 たとえば、詐欺Webサイトから同じリクエストを実行しようとするスクリプトは、「無効なCSRFトークン」エラーを受け取ります。

実際、ユーザーが最初に実際のWebサイトにアクセスしていない場合、Cookieは設定されず、要求は失敗します。

5. 結論

この記事では、RESTAPIに対するCSRF攻撃が可能かどうかを確認しました。

次に、SpringSecurityを使用してCSRF保護を有効または無効にする方法を学びました。