LoginSignup
14
4

More than 3 years have passed since last update.

【Rails】iframeタグを使って外部コンテンツが埋め込めない

Posted at

iframeで外部ページを埋め込む時の問題

ページに組み込んだiframeが表示されない問題についての対処法です。
ブラウザのコンソールをチェックしてみると、以下のエラーが表示されている場合があるかもしれません。

error
Refused to display xxxxx in a frame because it set 'X-Frame-Options' to SAMEORIGIN'.

これはRailsでデフォルトのセキュリティヘッダーのX-Frame-Options がSAMEORIGINになっているからです。

X-Frame-Options

X-Frame-Options は HTTP のレスポンスヘッダーで、ブラウザーがページを frame, iframe, embed, object の中に表示することを許可するかどうかを示すために使用されます。サイトはコンテンツが他のサイトに埋め込まれないよう保証することで、クリックジャッキング攻撃を防ぐために使用することができます。

参照:X-Frame-Options

 response.headers['X-Frame-Options'] = 'SAMEORIGIN'

SAMEORIGINではオリジンページに含まれているページのみフレームで表示することが可能という意味です。DENYにするとフレームにページが読み込まれないようになります。
ALLOW-FROM (uri)を使うと、指定されたuriのみがフレーム内で表示することが可能になります。

 response.headers['X-Frame-Options'] = 'DENY'
 response.headers['X-Frame-Options'] = 'ALLOW-FROM https://example.com'

しかしALLOW-FROMはブラウザ側の未サポートやバグがあり、非推奨となっています。chromeやFireFoxなどのメインブラウザで無効なので使わない方が良さそうです。
またALLOWALLというディレクティブがあるという記事もあります。(参照:Rails4: Allow your site to be iframed by another site.

 response.headers['X-Frame-Options'] = 'ALLOWALL'

しかし試したところ無効でした。MDNで確認したところ有効なディレクティブは現在SAMEORIGINDENYだけみたいです。

X-Frame-Optionsのまとめ

  • X-Frame-Options
    • DENY - ページをフレーム内に表示できなくなる
    • SAMEORIGIN - ページのドメインとフレームのドメインが同じ場合にのみ、ページがフレーム内で表示される。
    • ALLOW-FROM http://example.com (非推奨) - 指定されたURIのページのみ、フレーム内で表示される。
    • ALLOWALL (非推奨/無効) - どのページもフレーム内で表示される。

Content-Security-Policy (CSP)

コンテンツセキュリティポリシー (CSP) は、クロスサイトスクリプティング (XSS) やデータインジェクション攻撃などのような、特定の種類の攻撃を検知し、影響を軽減するために追加できるセキュリティレイヤーです。これらの攻撃はデータの窃取からサイトの改ざん、マルウェアの拡散に至るまで、様々な目的に用いられます。

CSP を有効にするには、ウェブサーバーから Content-Security-Policy HTTP ヘッダーを返すように設定する必要があります (X-Content-Security-Policy ヘッダーに関する記述が時々ありますが、これは古いバージョンのものであり、今日このヘッダーを指定する必要はありません)。

X-Frame-Optionsの代替方法

X-Frame-OptionsでALLOW-FROM uriALLOWALLを使えないので、その代替としてContent-Security-Policy(CSP)を使うことができそうです。CSPのナビゲーションディレクティブを用いると埋め込み先を管理することができます。
frame-ancestorsというディレクティブでによって埋め込み先のドメインを限定化し、そのドメインでのみの表示を許可することができます。

response.headers['Content-Security-Policy'] = "frame-ancestors https://example.com"

また例えばstaging環境を許容したいという場合は以下のような感じで書くことができます。

url = Rails.env.production? ? "https://example.com" : "https://staging.example.com"
response.headers['Content-Security-Policy'] = "frame-ancestors 'self' #{url}"

これはこのように書き換えることも可能です。

response.headers['Content-Security-Policy'] = "frame-ancestors 'self' https://*.example.com"

よって埋め込み元のcontrollerには以下のように記述すればOKです。

blogs_controller.rb
class BlogsController < ApplicationController
  after_action :allow_iframe, only: [:iframe]

  def iframe
    @blog = Blog.new
  end

  def allow_iframe
     response.headers['Content-Security-Policy'] = "frame-ancestors 'self' https://*.example.com"
  end
end

CSP frame-ancestorsの書き方まとめ

  • 埋め込み先を自身のドメイン (サブドメインを除く) に限定させたい場合。

例えばhttps://example.com/blogsページにhttps://example.com/articlesページを埋め込みたい時、親がhttps://example.comで同じなのでblogs(埋め込み元)のコントローラーに以下の処理を書けば親が共通のページはフレームで表示することができます。

blogs_controller.rb
response.headers['Content-Security-Policy'] = "frame-ancestors 'self'"
  • 自身のドメインとそのサブドメインへの埋め込みを許可したい場合。

例えばhttps://example.com/blogsと同じ親を持つhttps://example.com/articlesページと、サブドメインhttps://staging.example.comを親に持つhttps://staging.example.com/articlesページに埋め込みたい時、blogs(埋め込み元)のコントローラーに以下の処理を書けば埋め込み先のフレーム内に表示されます。

blogs_controller.rb
response.headers['Content-Security-Policy'] = "frame-ancestors 'self' https://*.example.com"

'self' はメインドメインを親に持つページ内への埋め込みを許可するという意味であり、https://*.example.comはサブドメイン(https://staging.example.com)を親とするページ内への埋め込みを許可するという意味になります。

CSPのまとめ

  • CSP
    • frame-ancestors - <frame>, <iframe>, <object>, <embed>, <applet>などの要素によって埋め込まれるページの親を指定し、埋め込みを有効にする。

X-Frame-Optionsのエラーは出るものの変更する必要はなく(というか使えるディレクティブがなく)、その代わりにCSPを指定する必要があったというのが学びでした。

14
4
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
14
4