Rails 6でiframeのCORSを許可するためにやったこと

RailsのCORS(オリジン間リソース共有)で特定のドメインからのiFrameのみ許可したい

モチベーション

自分のブログ上に、ブログのドメインと異なるサイトへリリースしたサービスを埋め込みたい。

例えばtoritakashi.comにexample.herokuapp.comのサイト(example.herokuapp.comへのリリース権限あり)を埋め込みたいケース。

ドメインを見るとわかりますが、このWebサービスはHeroku上にリリースしてあります。

直面した問題

オリジン間リソース共有が許可されておらず、iFrameで埋め込むことができない。

Refused to display 'http://example.herokuapp.com/' in a frame because it set 'X-Frame-Options' to 'sameorigin'.

のようにX-Frame-Optionsでsame originが設定されていて読み込めないことがわかります。

example.herokuapp.comにおいてtoritakashi.comによるリソース共有を、読み込み元(Origin)のドメインが異なるという理由で許可していないということです。

解決策

Access-Control-Allow-Origin: *であらゆるオリジンからリソースへアクセスすることを許可します。

そしてヘッダーのX-Frame-Optionsにallow-from uriを、Content-Security-Policyにframe-ancestors uriを設定します。

X-Frame-Optionsのallow-from uriは廃止されたディレクティブですが、ブラウザの後方互換のために書いておきます。

最新のブラウザはContent-Security-Policyのframe-ancestorsを参照し表示許可の判定をしています。

① rack-corsをインストール

Gemfileに

gem 'rack-cors'

を追加し

bundle install

②Access-Control-Allow-Originを設定

config/initializersに

Rails.application.config.middleware.insert_before 0, Rack::Cors do
  allow do
    origins '*'

    resource '*',
      headers: :any,
      methods: [:get, :post, :put, :patch, :delete, :options, :head]
  end
end

を追加する。

③X-Frame-OptionsとCSPをいじる

許可したいアクションの記述されたコントローラファイルを開き、response.headersを書き換える処理を追加します。

Webサービス全体に対して設定したいときはApplicationControllerに書くなり(未検証)してください。

class HogeHogeController < ApplicationController
  after_action :allow_iframe, only: [:index]

  def index
  end

  private
  def allow_iframe
    url="http://example.herokuapp.com"
    response.headers['X-Frame-Options'] = "ALLOW-FROM #{url}"
    response.headers['Content-Security-Policy'] = "frame-ancestors #{url}"
  end
end

解決

リリースブランチにPushして…

無事表示されるようになりました。

めでたしめでたし。

参考