LoginSignup
23
21

More than 3 years have passed since last update.

SPA + WEB API のCSRF対策とCORSについて

Last updated at Posted at 2020-05-13

この記事はSPA + WEB_APIで構築するWEBサービスのセキュリティの脆弱性を考える時に必ず出てくる、CSRF対策やCORSの考察をした記録です。
SPAでWEBサイトを構築する方、または既に持っていて脆弱性がないか調べている時にお役に立てる内容となります。

CSRFについてはこちらのサイトがとてもわかりやすいです。
https://qiita.com/okamoai/items/044c03680766f0609d41

大前提

ブラウザで非同期通信を行う際には、同一生成元ポリシー(Same Origin Policy)によってWebページを生成したドメイン以外へのHTTPリクエストができません。

SPA + WEB_APIのパターンなど異なるドメインのリソースにアクセスしたいという需要があります
→ 昔はJSONP使ってたがセキュリティだめ
→ より手軽に、より安全にクロスドメインアクセスを実現したいという要求に応えるために作られたのがCORSという手法

CORSの設定方法

  • クロスドメインアクセスされるサーバー側(=API)でアクセスを制御するルールを設定
  • ブラウザとサーバー側でHTTPヘッダを使ってアクセス制御に関する情報をやりとりしながらドメインをまたいだアクセスをする
  • サーバー側で設定するアクセスを制御するルールは下記
    • クロスドメインアクセスを許可するWebページのオリジンサーバーのドメイン(通常SPAドメインを指定)
    • 使用を許可するHTTPメソッド(GET,POSTなど)
    • 使用を許可するHTTPヘッダ

実際の設定例

  • AWS S3
<?xml version="1.0" encoding="UTF-8"?>
<CORSConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
<CORSRule>
    <AllowedOrigin>https://sample-domain.cdn.com</AllowedOrigin>
    <AllowedMethod>GET</AllowedMethod>
    <MaxAgeSeconds>3000</MaxAgeSeconds>
    <AllowedHeader>*</AllowedHeader>
</CORSRule>
</CORSConfiguration>
  • Rails API rack-cors使用
Rails.application.config.middleware.insert_before 0, Rack::Cors do
  allow do
    origins Rails.application.config.x.cors_allowed_origins

    resource 'sample-domain.cdn.com',
             headers: :any,
             methods: %i[get post put patch delete options head]
  end
end

CORSのチェックはどういう時に行われる?

  • 非同期かつクロスオリジンリクエストの場合のみ行われる、プリフライトリクエスト でCORSチェックを行う。http methodは OPTIONS

  • 必ずプリフライトリクエストを伴うようになっていればよいということ。(非同期でのoriginヘッダーの偽装はできない)

  • プリフライトリクエストが起こらない単純リクエストというのもある。

  • SPA + APIのパターンでは Content-Type: application/json にすることが多い。→ 原則としてプリフライトリクエストを伴う。

  • API側でリクエストの受付をapplication/jsonのみにしておくと確実である。

CORSのチェックが行われない場合はどういうとき?

  • 同期リクエスト(curlなどでAPI叩いたり、ブラウザの標準リクエスト)

  • この場合CORSチェックを伴わないAPIリクエストが可能になってしまう == CSRF攻撃が可能

CSRF対策

同期API直リクエストや非同期の単純リクエストの際に起こりうるCSRF、その対策はどうすれば良いか?

それぞれのリクエストの特徴がある!

非同期リクエスト
- カスタムヘッダを追加できる
- プリフライトリクエストを伴うことが可能

同期リクエスト
- カスタムヘッダの追加が不可能(標準ヘッダの変更しかできない)

対策方法

CSRF対策はいくつかの方法があります。

  1. CSRFトークンを使用した方法
  2. プリフライトリクエストとカスタムヘッダを使用
  3. Cookieの SameSite オプションを使用

3については下記サイトをご参照ください。ここでは主に2の方法を説明します。
https://qiita.com/okamoai/items/044c03680766f0609d41

2. プリフライトリクエストとカスタムヘッダを使用
  • 同期リクエストはそもそもできなくしたい
    • → APIサーバーでカスタムヘッダの検証をさせ、非同期リクエストではカスタムヘッダを付与してリクエストする。同期リクエストはカスタムヘッダの追加ができないので全て防ぐことが可能。
    • → カスタムヘッダを下記のようにすることで 1. CSRFトークン も合わせて可能
# APIで生成し保存されているユーザー固有のランダム文字列
request.headers: { x-csrf-token: 'ランダム文字列', ... }
  • 非同期リクエストへの対応
    • → APIでapplication/jsonのみ受付け可能にする = プリフライトリクエストが必ず行われる
    • → APIでCORS設定を行う(許可したオリジンアクセスのみリクエストできる)

あとがき

WEB API + SPA パターンは先行してWEBを作成、後追いでアプリを作る場合などにもよく使われます。
最近ではかなり案件数も増えている構成なので、これを気にちゃんと勉強できてよかったです。

ここまで読んでくださいまして、ありがとうございました!
誤りなどあれば、問題になることもあると思いますのでご指摘いただけると幸いです!

参考にさせていただいた記事

Web API の CSRF 対策まとめ【追記あり】
MDN WEB Docs オリジン間リソース共有 (CORS)

23
21
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
23
21