CORSが必要な理由とセキュリティリスクを初心者向けにやさしく解説!Spring Securityでの対策も紹介
新人
「先輩、Reactで作ったフロントエンドからSpringのAPIにアクセスしようとしたら、CORSのエラーが出たんですけど……これって何が問題なんですか?」
先輩
「それはよくある悩みだね。CORS、つまりクロスオリジンリクエストの制限に引っかかったんだよ。」
新人
「クロスオリジンって、なんとなく聞いたことはあるんですけど、よくわからなくて……」
先輩
「じゃあ今回は、CORSの必要性やセキュリティリスクについて、初心者でもわかりやすく解説していこうか。」
1. CORSが必要とされる背景(セキュリティ観点からの導入)
Webアプリケーションを開発していると、フロントエンドとバックエンドが別サーバーで動作することがよくあります。たとえば、http://localhost:3000でReactアプリを起動し、http://localhost:8080でSpringアプリを実行しているようなケースです。
このとき、JavaScriptが別ドメインにあるAPIにアクセスしようとすると、ブラウザがセキュリティのためにブロックする場合があります。これがCORS(Cross-Origin Resource Sharing)という仕組みによる制限です。
このCORSは、セキュリティ上とても重要な役割を果たしています。もし制限がなければ、悪意のあるサイトがユーザーの認証情報を使って勝手にリクエストを送信することが可能になってしまいます。
たとえば、あるユーザーがECサイトにログインした状態で、別の悪意あるWebサイトを開いたとします。そのWebサイトが自動的にユーザーのクッキー情報を使って、商品を購入したり、情報を取得したりできてしまう可能性があるのです。
そこで、ブラウザは「異なるオリジン(=ドメイン・ポート・プロトコルが異なる)からのリクエストは一旦ブロックし、サーバーが許可しているかを確認する」という動作をするようになっています。
このように、「CORS セキュリティ」はWebの世界で非常に重要なテーマであり、SpringでWeb開発をする際も必ず知っておくべき知識なのです。
2. クロスオリジンの意味と、どんなケースで発生するのか
クロスオリジンリクエストとは、別のオリジン(Origin)からリソースにアクセスしようとすることです。「オリジン」は、スキーム(http/https)、ドメイン、ポート番号の3つの組み合わせで構成されます。
次のようなパターンは、すべてオリジンが異なると判断され、CORSの制限対象になります:
- httpとhttpsが異なる:http://example.com と https://example.com
- ポート番号が異なる:http://localhost:8080 と http://localhost:3000
- サブドメインが異なる:https://api.example.com と https://www.example.com
たとえば、以下のようなケースはすべて「クロスオリジンリクエスト」として扱われます:
http://localhost:3000(Reactアプリ)→http://localhost:8080(Springアプリ)https://myapp.com(フロントエンド)→https://api.myapp.com(バックエンド)http://myapp.com→https://myapp.com
このようなリクエストは、ブラウザがセキュリティ上の観点から制限をかけます。
つまり、フロントエンドとバックエンドを別サーバーや別ポートで開発していると、意図せずCORSエラーに遭遇することが多くなります。
そこで、Spring Securityやコントローラで適切にCORSを許可する設定を行う必要があるのです。これがまさに「CORS Spring セキュリティ」というキーワードが検索される理由です。
では次回は、CORSが存在しない場合にどのようなセキュリティリスクがあるのか、CSRFとの違いなども含めて詳しく見ていきましょう。
3. CORSがないとどうなる?(セキュリティリスクの実例)
もしCORSの仕組みが存在しなかったら、私たちが普段安心して使っているWebサービスは、大きな危険にさらされることになります。
たとえば、あるユーザーが銀行のWebアプリケーションにログインして、ブラウザにログイン情報(クッキーなど)が保存されているとします。その状態で、悪意のある別サイト(例:http://malicious.example)を開いた場合、そのサイトがJavaScriptで勝手にユーザーの銀行APIへリクエストを送ることが可能になります。
このとき、銀行のAPIは「ログイン済みのクッキーがある」と判断し、リクエストを正当なものとして受け入れてしまう可能性があります。結果として、知らないうちに送金処理が行われたり、個人情報が取得されたりする恐れがあります。
これは実際に起こりうる「クロスオリジン攻撃」の一例であり、特に金融機関や個人情報を扱うシステムでは致命的な被害につながる可能性があります。
そのため、CORSのような仕組みで「このリクエストは正当なオリジンからのものか?」を確認することは、Webアプリケーションにおいて非常に重要なのです。
4. 攻撃パターン:CSRFとの違いと関係性
セキュリティリスクとしてよく知られている「CSRF(クロスサイトリクエストフォージェリ)」も、CORSと深く関係しています。名前が似ているため混同されやすいですが、それぞれ異なる攻撃手法です。
CSRF攻撃は、ユーザーが意図しないリクエストを送信させることにより、勝手に処理を実行させる攻撃です。
たとえば、あるユーザーがショッピングサイトにログインしている状態で、別の悪意あるサイトを開いたとします。その悪意あるサイトに以下のようなコードが埋め込まれていたらどうなるでしょうか:
<form action="https://shop.example.com/api/order" method="POST">
<input type="hidden" name="item" value="高額商品">
<input type="hidden" name="amount" value="1">
<input type="submit">
</form>
<script>
document.forms[0].submit();
</script>
このコードは自動的に注文を実行してしまうものです。ユーザーは何も操作していないのに、裏で勝手に注文処理が行われてしまいます。
このような攻撃が可能になる背景には、「ログイン中のクッキー情報が自動で送信されてしまう」という挙動があります。CSRFはこれを悪用するのです。
一方、CORSはJavaScriptのAPI通信に対する制限をかける仕組みであり、XMLHttpRequestやfetchによるクロスオリジン通信を制御するものです。フォーム送信に関してはCORSの対象外のため、CSRF対策としては不十分です。
そのため、CORSとCSRFは異なる性質の攻撃ですが、両方に対策を施す必要があります。CSRFにはトークンを使った防御策、CORSにはヘッダーによるオリジン制限などが必要となります。
これらを組み合わせることで、より強固なWebセキュリティを実現することができるのです。
5. フロントエンドとバックエンドの通信におけるCORSの重要性
最近では、フロントエンドとバックエンドが分離された構成(いわゆるSPA:シングルページアプリケーション)が主流になっています。ReactやVue.jsで画面を構築し、Spring BootでAPIを提供するようなスタイルが一般的です。
このような構成では、クロスオリジン通信が避けられません。つまり、別々のドメインやポートをまたいで通信を行う必要があります。
しかしそのままでは、ブラウザのセキュリティ制限により「Access-Control-Allow-Origin」ヘッダーが存在しないとして通信が拒否されることになります。
Spring Securityを使用する場合、CORS設定はセキュリティの構成内で行う必要があります。設定を忘れると、たとえAPI自体は正常に動いていても、ブラウザがアクセスを許可してくれないという事態になります。
CORSを正しく設定することで、フロントエンドとバックエンドがスムーズに連携できるようになります。たとえば、Spring Securityの設定クラスで以下のようにCORSの設定を追加することができます:
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.cors().and()
.csrf().disable()
.authorizeHttpRequests(authz -> authz
.anyRequest().permitAll()
);
return http.build();
}
@Bean
public CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration configuration = new CorsConfiguration();
configuration.setAllowedOrigins(List.of("http://localhost:3000"));
configuration.setAllowedMethods(List.of("GET", "POST", "PUT", "DELETE"));
configuration.setAllowCredentials(true);
configuration.setAllowedHeaders(List.of("Authorization", "Content-Type"));
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", configuration);
return source;
}
}
このように設定することで、http://localhost:3000からのクロスオリジン通信を許可することができ、CORSエラーを回避できます。
「CORS セキュリティリスク」や「クロスオリジン攻撃」「CSRF 違い」といったキーワードが頻繁に検索されるのは、まさにこうした背景があるからなのです。
次回は、実際にSpring SecurityでCORSを設定する方法を、コードを交えてさらに詳しく見ていきましょう。
6. Spring SecurityでCORSを制御する際の注意点
Spring SecurityでCORSを制御する際には、ただ設定を追加するだけでは不十分です。特に初心者がやりがちなミスは、SecurityFilterChainとCorsConfigurationSourceの連携を正しく設定しないことです。
Spring Securityでは、セキュリティ設定とCORSの設定が分かれているため、両方に正しく定義しなければCORSが機能しないという点に注意が必要です。
たとえば、セキュリティ設定の中でhttp.cors()と明示しないと、CorsConfigurationSourceの内容が反映されないことがあります。
また、デフォルトではAllow-Credentialsが無効になっているため、クッキーや認証情報を送信する通信ではsetAllowCredentials(true)の設定を忘れずに行いましょう。
さらに、CORS設定を適用したつもりでも、Controllerで@CrossOriginを使ってしまうとセキュリティフィルタより前に処理が流れてしまうことがあり、正しく制御されないケースもあります。
つまり、「CORS Spring セキュリティ」を正しく実装するためには、セキュリティ構成クラスにおいて、CORSの設定とその有効化を明示的に行うことが重要なのです。
7. 許可設定の誤りが引き起こすセキュリティホールとは
CORSの設定ミスは、セキュリティ上重大なリスクを招くことがあります。特にありがちな誤りは、*(ワイルドカード)を許可してしまうことです。
たとえば、以下のような設定をしてしまうと、すべてのオリジンからのアクセスを無制限に許可することになり、攻撃者にもAPIを自由に呼び出す手段を与えてしまうことになります:
configuration.setAllowedOrigins(List.of("*")); // これは非常に危険
この設定を使った場合、Access-Control-Allow-Originヘッダーには*が付与され、すべてのドメインからのリクエストを許可することになります。
特にAllow-Credentialsをtrueにしながら、*を同時に使用すると、Spring Securityの仕様上エラーになるため、実行時に問題が発生します。
しかも、もしこれが本番環境であれば、悪意のあるフロントエンドからAPIが自由に呼び出され、情報漏洩や不正操作が発生する恐れがあります。
このような設定ミスは「CORS設定 ミス」や「CORS セキュリティホール」として、開発者の間で度々問題視されています。
そのため、開発時には必ず以下の点をチェックしましょう:
- 本番環境では
*を使わず、明示的に許可するドメインを指定する Allow-Credentialsと併用する場合は、ドメインを固定する- 不要なHTTPメソッド(PUTやDELETEなど)は最小限に制限する
これらを怠ると、CORSが逆にセキュリティホールになってしまうのです。
8. CORS設定のベストプラクティス(許可すべきオリジンの範囲、開発と本番の切り分け)
CORSを正しく設定するには、開発環境と本番環境を明確に分けた設定を行うことが重要です。開発中はローカルのReactアプリなどからアクセスできるようにしておき、本番では本当に必要なドメインだけを許可するようにしましょう。
たとえば、開発中は次のように設定します:
configuration.setAllowedOrigins(List.of("http://localhost:3000"));
一方、本番環境では以下のように安全なドメインに限定します:
configuration.setAllowedOrigins(List.of("https://myapp.com"));
このように環境に応じて設定を切り替えるためには、プロファイル(spring.profiles.active)を活用すると良いでしょう。
また、CORS設定で注意すべき点として、許可するヘッダーとメソッドを明示的に定義することが挙げられます。以下のようにして必要なヘッダーやメソッドのみ許可することで、セキュリティリスクを最小限に抑えることができます:
configuration.setAllowedMethods(List.of("GET", "POST"));
configuration.setAllowedHeaders(List.of("Authorization", "Content-Type"));
さらに、パス単位でCORSの設定を変更することも可能です。たとえば、APIの中でも特に認証が必要なエンドポイントには、より制限の厳しい設定を適用するという方法もあります。
このように、細かな制御を加えることで、CORS Spring セキュリティの設定はより安全かつ柔軟に運用することができるのです。
セキュリティと利便性のバランスをとりながら、環境ごとに適切なCORSの構成を心がけましょう。