🖥

#Rails + rack-cors で CORS の ORIGINS を指定 / curl で動作確認 / request spec でテ

2020/03/25に公開

Gem

rack-cors

cyu/rack-cors: Rack Middleware for handling Cross-Origin Resource Sharing (CORS), which makes cross-origin AJAX possible.

CORS とは

  • リソース共有のための仕組み。
  • CORSを指定したからと言ってセキュリティ対策になるわけではなく、むしろ逆。ドメインを共有できるようになるので、セキュリティリスクは増す。
  • CORSを許可する場合は、許可する ORIGINとしてワイルドカードを指定せず、可能であればホワイトリストを指定する。
  • セキュリティ的なデフォルト挙動が「同一オリジンポリシー」に準拠したものであり、CORSがその間口を広げる。

設定

  • Rails構成のAPIサーバーを想定
  • localhost:3000 で API サーバーがつながるものとする
  • application.rbに以下を設定してサーバーを再起動する
module App
  class Application < Rails::Application
    # Rails 5

    config.middleware.insert_before 0, Rack::Cors do
      allow do
        # いろいろな形式で、複数指定できる
        origins 'example.com', 'https://yahoo.com', 'localhost:9999'
        resource '*', headers: :any, methods: [:get, :post, :options]
      end
    end
end

curl での動作確認

  • リクエストヘッダの Origin として Rails で 許可した ORIGIN を指定してリクエストする
  • 実際のリクエスト元は 自分が利用しているシェルであり、 http://example.com ではないわけだが、ヘッダに指定することは可能だ。
curl --dump-header - "http://localhost:3000/v1/foo" -H "Origin: http://example.com"   -H "Content-Type: application/json" HTTP/1.1 200 OK

結果の例

HTTP/1.1 200 OK
Access-Control-Allow-Origin: https://example.com
Access-Control-Allow-Methods: GET, POST, OPTIONS
...
  • プロトコルを問わずにORIGINが許可されているようだ。
  • https や ftp のURLを ORIGIN に指定しても Access-Control-Allow-Origin が返ってくる。
curl --dump-header - "http://localhost:3000/v1/foo" -H "Origin: https://example.com"   -H "Content-Type: application/json" HTTP/1.1 200 OK
HTTP/1.1 200 OK
Access-Control-Allow-Origin: https://example.com
Access-Control-Allow-Methods: GET, POST, OPTIONS

注意

  • Origin: http://example.com/ のように末尾にスラッシュをつけると Access-Control-Allow-Origin が返ってこない。
  • たとえば Railsの設定で https だけを許可した場合、 リクエストで Origin に http を申告しても、Access-Control-Allow-Origin は返ってこない。

リクエストヘッダの Orign とは何なのか

  • あくまでもクライアントの自己申告のようだ。実際のリクエスト送信元がなんであれ、自由に指定できる。別にリクエスト元のIPなどから生成されているわけではない。
  • 仕様はクライアントによるはずだ。たとえばブラウザからのリクエスト送信であれば「Originを超えた、自分以外の場所にリクエストをする場合に、自動的に Origin ヘッダに、送信元の正しい Origin を付与する」というような仕様だと思うのだけれど。
  • シェルの場合はクライアントとしての仕様があるわけではないので Origin の指定も自由だ。なので、リクエストヘッダとして Origin の指定しなければ、そもそも Origin のヘッダは送信されないみたいだ。たとえそれが Origin を超えたリクエストだろうと、なんだろうと。
  • ヘッダを詐称するクライアントの攻撃は防ぐことはできないはずだ。

そんな自己申告の仕組みが脆弱性、セキュリティ対策とどう関係するのだろうか?

  • CORSで外部リクエストを許可していた場合、第三者が踏み台のサイトを用意して、善意のユーザーがサーバーにリクエストするような処理を作っていた時に、脆弱性が生まれる場合がある。(クロスサイト系の攻撃って、そういうものですよね) (直接攻撃じゃなくて)
  • ユーザーのクライアントであるブラウザは、悪意のある第三者のサイトから、サーバーにリクエストする場合でも、詐称しない、正しいOriginヘッダを投げる。この時にフロント側の制御として、悪意のある送信がおこなわれないようにブロックするということだろうか。

Rspec でリクエストのテストを書く

こんな感じで出来るはず

require "rails_helper"

RSpec.describe  type: :request do
  subject do
    get "/v1/foo", headers: { Origin: "http://example.com" }
  end

  it do
    subject
    expect(response.header["Access-Control-Allow-Origin"]).to eq "http://example.com"
  end
end

参考

CORSまとめ - Qiita

CORS (Cross-Origin Resource Sharing) ってなに?

Original by Github issue

https://github.com/YumaInaura/YumaInaura/issues/3048

チャットメンバー募集

何か質問、悩み事、相談などあればLINEオープンチャットもご利用ください。

https://line.me/ti/g2/eEPltQ6Tzh3pYAZV8JXKZqc7PJ6L0rpm573dcQ

Twitter

https://twitter.com/YumaInaura

公開日時

2020-03-25

Discussion