この文書は2016年以降更新されていません

クリックジャッキング対策に関するチートシート

OWASP 作成
ジャンプ先: 移動検索
Cheatsheets-header.jpg


最終改訂日 (yy/mm/dd):2015/02/11

はじめに

このチートシートは、クリックジャック / UI Redress 攻撃を回避するためのガイダンスを開発者に提供することに重点を置いています。

クリックジャッキングを防ぐ最も一般的な方法は、防御が必要なサイトが、他の Web ページ上のフレーム内で表示されないようにする、何らかの "フレームブレーキング" 機能を組み込むことです。このチートシートでは、フレームブレーキングを実装する方法として、 X-Frame-Options ヘッダー (ブラウザーが対応している場合に使用) と、JavaScript による実装について説明します。

CSP frame-ancestors ディレクティブを使用した対策

Content Security Policy の HTTP レスポンスヘッダーで frame-ancestors ディレクティブを使用すると、ブラウザーで <frame> または <iframe> を使用してページをレンダリング可能にするかどうかを指定できます。これをサイトで使用すると、コンテンツが他のサイトに埋め込まれなくなるため、クリックジャッキング攻撃を回避できます。

frame-ancestors は、通常の Content Security Policy symantics を使用して、複数のドメインをサイトが承認できるようにします。

詳細については、https://w3c.github.io/webappsec/specs/content-security-policy/#directive-frame-ancestors を参照してください。

制限事項

  • ブラウザーのサポート: 現時点では、frame-ancestors は主要ブラウザーすべてではサポートされていません。
  • X-Frame-Options の優先度: CSP Spec のセクション 7.7.1 では、frame-ancestors を指定すると X-Frame-Options は無視されると説明されていますが、Chrome 40 と Firefox 35 では、frame-ancestors ディレクティブを無視して、X-Frame-Options ヘッダーに従うようになっています。

X-Frame-Options レスポンスヘッダーを使用した対策

X-Frame-Options HTTP レスポンスヘッダーを使用すると、ブラウザーがページを <frame> または <iframe> 上でレンダリング可能にするかどうかを示すことができます。これをサイトで使用すると、コンテンツが他のサイトに埋め込まれなくなるため、クリックジャッキング攻撃を回避できます。

X-Frame-Options ヘッダーの種類

X-Frame-Options ヘッダーには、3 種類の使用可能な値があります。

  • DENY: フレーム内でのコンテンツ表示を全ドメインで拒否します。
  • SAMEORIGIN: フレーム内でのコンテンツ表示を現在のサイトでのみ許可します。
  • ALLOW-FROM uri: フレーム内でのコンテンツ表示を指定された「uri」に対し許可します(例、ALLOW-FROM http://www.example.com)。ブラウザーがサポートしていない場合、この制限には効果はありません。以下の制限を確認してください

ブラウザーのサポート

X-Frame-Options ヘッダーをサポートしているブラウザーを次に示します。

ブラウザー DENY/SAMEORIGIN サポート導入済み ALLOW-FROM サポート導入済み
Chrome 4.1.249.1042 サポートしていない / バグが報告されている
Firefox (Gecko) 3.6.9 (1.9.2.9) 18.0
Internet Explorer 8.0 9.0
Opera 10.50
Safari 4.0サポートしていない / バグが報告されている

参考資料:

実装

この保護を実装するには、フレームバスティングによるクリックジャッキングから保護する必要のあるページに X-Frame-Options HTTP レスポンスヘッダーを追加する必要があります。その方法の 1 つが各ページに手動で HTTP レスポンスヘッダーを追加することです。もっと簡単なのは、各ページにヘッダーを自動的に追加するフィルターを実装する方法です。

OWASP では、このフィルターを Java EE 環境で実装するための詳細を記事とコードで提供しています。

SDL ブログには、これを .NET環境で実装する方法を説明した記事があります。

よくある対策の失敗

Meta タグによる X-Frame-Options ディレクティブの適用は機能しません。たとえば、< meta http-equiv="X-Frame-Options" content="deny">) は機能しません。前述のように、X-Frame-Options ディレクティブは HTTP レスポンス ヘッダーとして適用する必要があります。

制限事項

  • ページごとのポリシー指定: ポリシーをページごとに指定する必要があります。これにより、展開が複雑になることがあります。ログイン時にこれをサイト全体に強制する機能を提供することなどにより、適用をシンプル化できます。
  • マルチドメインサイトの問題: 現在の実装では、Web 管理者がフレーム内でのページ表示を許可するドメインのホワイトリストを提供することはできません。ホワイトリストには危険が伴う可能性がありますが、場合によっては Web 管理者が複数のホスト名を使用する以外に方法がない場合もあります。
  • ALLOW-FROM のブラウザーでのサポート: ALLOW-FROM オプションは、比較的最近 (2012 年頃) 追加されました。まだすべてのブラウザーではサポートされていない可能性があります。ALLOW-FROM の使用には注意してください。ブラウザーに ALLOW-FROM を適用しても、そのブラウザーが ALLOW-FROM をサポートしていない場合は、クリックジャッキングの防御はできません。
  • 複数オプションの非サポート: 現在のサイトとサードパーティーのサイトにおいてフレーム内での同じ応答の表示を許可する方法はありません。ブラウザーは 1 つの X-Frame-Options ヘッダーとそのヘッダーの 1 つの値のみを許可します。
  • 入れ子になったフレームにおける SAMEORIGIN および ALLOW-FROM の働き: 次に示す状況では、http://framed.invalid/child フレームはロードされません。ALLOW-FROM が直接の親ではなく、トップレベルのブラウジングコンテキストに適用されるからです。これを解決するには、親フレームと子フレームの両方で ALLOW-FROM を使用します (ただし、これは //framed.invalid/parent ページがトップレベルドキュメントとして読み込まれると、子フレームのロードを妨害します)。
+-//friendlysite.invalid-----------------------+
|                                              |
| +-//framed.invalid/parent------------------+ |
| |                                          | |
| | ALLOW-FROM http://friendlysite.invalid | |
| |                                          | |
| | +-//framed.invalid/child--------+        | |
| | |                               |        | |
| | | SAMEORIGIN                    |        | |
| | |                               |        | |
| | +-------------------------------+        | |
| +------------------------------------------+ |
+----------------------------------------------+
  • X-Frame-Options の非推奨: X-Frame-Options ヘッダーは主要ブラウザーでサポートされていますが標準化されていません。CSP レベル 2 仕様で frame-ancestors ディレクティブが策定され、X-Frame-Options は非推奨とされました。
  • プロキシ: Web プロキシは、ヘッダーを追加/削除するので厄介です。Web プロキシが X-Frame-Options ヘッダーを削除した場合、サイトはフレーム内でのページ表示禁止による保護を失います。

現時点で最適なレガシブラウザーのフレームブレーキングスクリプト

クリックジャッキングに対する防御策の 1 つに、フレーム内での表示を禁止する必要のあるページごとに "フレームブレーカー" スクリプトを含める方法があります。次に示す方法では、X-Frame-Options ヘッダーをサポートしていないレガシブラウザーでも、フレーム内での Web ページ表示を禁止することができます。

ドキュメントの HEAD 要素に、次のように追加します。

まず、style 要素自体に ID を適用します。

<style id="antiClickjack">body{display:none !important;}</style>

そのすぐ後に、その ID を使用して style を削除します。

<script type="text/javascript">
   if (self === top) {
       var antiClickjack = document.getElementById("antiClickjack");
       antiClickjack.parentNode.removeChild(antiClickjack);
   } else {
       top.location = self.location;
   }
</script>

この方法では、すべてをドキュメントの HEAD に入れることができ、API に 1 つのメソッド / タグライブラリーのみがあればすみます。

参考資料:https://www.codemagi.com/blog/post/194

window.confirm() での保護

X-Frame-Options やフレームブレーキングスクリプトを使用することは、クリックジャッキングからの保護としては、より安全性の高い方法になります。ただし、フレーム内でのコンテンツ表示が必要なシナリオでは、window.confirm() を使用して、実行しようとしているアクションをユーザーに通知することで、クリックジャッキングを軽減することができます。

window.confirm() を呼び出すと、フレーム内で表示できないポップアップが表示されます。Window.confirm() を呼び出した iframe のコンテンツのドメインが親とは別であった場合、ダイアログボックスには、window.confirm() の元になるドメインが表示されます。このシナリオでは、ブラウザーはダイアログボックスの元を表示して、クリックジャッキング攻撃を緩和する支援をします。ただし、Internet Explorer だけは、window.confirm() のダイアログボックスに、元になるドメインが表示されません。この問題を Internet Explorer で解決するには、実行されているアクションの種類に関するコンテキスト情報がダイアログボックス内のメッセージに含まれるようにする必要があるので、注意してください。次に例を示します。

<script type="text/javascript">
   var action_confirm = window.confirm("Are you sure you want to delete your youtube account?")
   if (action_confirm) {
       //... アクションを実行
   } else {
       //...ユーザーは要求されたアクションを実行する必要がありません。
   }
</script>

機能しないスクリプト

次のスニペットについて考えてみます (これはクリックジャッキングに対する防御としてはお勧めできないものです)。

<script>if (top!=self) top.location.href=self.location.href</script>

この簡単なフレームブレーキングスクリプトでは、現在のフレームの URL を強制的に親ウィンドウに読み込ませることで、ページが frame または iframe に組み込まれるのを回避しようとします。残念ながら、この種類のスクリプトを無効にする複数の方法が、既に公開されています。そのうちのいくつかを簡単に説明します。

二重のフレーム

一部のフレームバスティング技法では、parent.location に値を割り当てることにより、適切なページに移動します。被害者ページが単一ページ上でフレーム内表示される場合、これは正しく機能します。しかし、攻撃者が被害者を別のフレーム内のもう 1 つのフレームで囲むと (二重フレーム)、parent.location へのアクセスは子孫フレームナビゲーションポリシー (descendant frame navigation policy) により、一般的なブラウザーのすべてでセキュリティ違反になります。このセキュリティ違反は、カウンターアクションナビゲーションを無効にします。

被害者のフレームバスティングコード:

if(top.location!=self.locaton) {
  parent.location = self.location;
}

攻撃者のトップフレーム:

<iframe src="attacker2.html">

攻撃者のサブフレーム:

<iframe src="http://www.victim.com">

onBeforeUnload イベント

ユーザーは、フレーム内表示されたページによって送信されたナビゲーションリクエストを手動でキャンセルできます。これを攻略するために、フレーム内表示されるページは onBeforeUnload ハンドラーを登録します。このハンドラーは、ナビゲーションが原因でフレーム内表示されるページがアンロードされるたびに呼び出されます。ハンドラー関数は、ユーザーに表示されるプロンプトの一部になる文字列を返します。たとえば、攻撃者が PayPal フレーム内表示しようと考えているとします。攻撃者は「PayPal を終了しますか?」という文字列を返す unload ハンドラー関数を登録します。この文字列がユーザーに表示されると、PayPal のフレームバスティングの試みが無効にされ、ナビゲーションがキャンセルされる可能性があります。

攻撃者は次のコードを使用して、トップ ページで unload イベントを登録することにより、この攻撃を行います。

<script>
window.onbeforeunload = function()
{
  return "Asking the user nicely";
}
</script>
<iframe src="http://www.paypal.com">

PayPal のフレームバスティングコードが BeforeUnload イベントを生成することで上記の関数が呼び出され、ユーザーにナビゲーションイベントのキャンセルを促します。

No-Content フラッシュ

前の攻撃ではユーザーとの対話が必要でしたが、同じ攻撃をユーザーのアクションなしで行うことができます。ほとんどのブラウザー (IE7、IE8、Google Chrome、および Firefox) では、攻撃者は \204 - No Content で応答しているサイトにナビゲーションリクエストを繰り返し送信することで、onBeforeUnload イベントハンドラーに送信されてくるナビゲーションリクエストを自動的に取り消すことができます。No Content サイトへのナビゲートは事実上の NOP であり、リクエストのパイプラインをフラッシュして、元のナビゲーションリクエストをキャンセルします。これを実行するサンプルコードを次に示します。

var preventbust = 0
window.onbeforeunload = function() { killbust++ }
setInterval( function() {
  if(killbust > 0){
  killbust = 2;
  window.top.location = 'http://nocontent204.com'
  }
}, 1);
<iframe src="http://www.victim.com">

XSS フィルターの攻略

IE8 と Google Chrome では、Web ページを特定のタイプの XSS 攻撃から保護するために役立つ反射型 XSS フィルターを導入しました。Nava と Lindsay は Blackhat カンファレンスの発表において、これらのフィルターがフレームバスティングコードの迂回に使用できることを確認しました。IE8 XSS フィルターは、指定されたリクエストパラメーターを一連の正規表現と比較して、クロス サイトスクリプトでの明らかな攻撃を探します。"誘導されたフォールスポジティブ" を使用することで、フィルターを使用して特定のスクリプトを無効にできます。リクエストパラメーターのすべての script タグの先頭を照合することで、XSS フィルターはページ内のすべてのインラインスクリプト (フレームバスティングスクリプトを含む) を無効にします。外部インクルードと照合することにより、すべての外部スクリプトも無効にできます。読み込まれる JavaScript のサブセットは、まだ機能し (インラインまたは外部で)、クッキーもまだ使用可能なので、この攻撃はクリックジャッキングに有効です。

被害者のフレームバスティングコード:

<script>
if(top != self) {
  top.location = self.location;
}
</script>

攻撃者:

<iframe src="http://www.victim.com/?v=<script>if''>

XSS フィルターは、そのパラメーター "<script>if" を被害者のフレームバスティングスクリプトの先頭と一致させ、その結果、被害者のページにあるインラインスクリプトをすべて無効にします (フレームバスティングスクリプトを含む)。Google Chrome で使用できる XSSAuditor フィルターは、同じ攻略を有効にします。

top.location の上書き

最新のブラウザーの中には、location 変数を、すべてのコンテキストで特別な不変の属性として扱うものがあります。ただし、location 変数の再定義が可能な IE7 と Safari 4.0.4 は別です。

IE7 フレーム内表示されるページが location を再定義すると、top.location を読み込もうとするサブフレームのフレームバスティングコードは別のドメインでローカル変数を読み込もうとして、セキュリティ違反をコミットします。同様に、top.location を割り当てることでナビゲートを行う試みも失敗します。

被害者のフレームバスティングコード:

if(top.location != self.location) {
  top.location = self.location;
}

攻撃者:

<script> var location = "clobbered";
</script>
<iframe src="http://www.victim.com">
</iframe>

Safari 4.0.4

ほとんどの状況で location は不変に保たれますが、defineSetter を使用して location を設定するメソッドをカスタマイズすると(ウィンドウを通じて)、オブジェクトの場所は未定義になることがわかりました。フレーム内表示されるページは、以下のみを実行します。

<script>
  window.defineSetter("location" , function(){});
</script>

これで、トップフレームの場所を読み込んだり、ナビゲートしようとする試みは失敗します。

制限付きゾーン

ほとんどのフレームバスティング手法は、ページ表示がフレーム内で行われることを Javascript で検出して阻止します。JavaScript がサブフレームのコンテキストで無効にされると、フレームバスティングコードは実行されません。残念ながら、サブフレームで JavaScript を制限する方法はいくつかあります。

  • In IE 8:
    <iframe src="http://www.victim.com" security="restricted"></iframe>
  • In Chrome:
    <iframe src="http://www.victim.com" sandbox></iframe>
  • Firefox および IE: 親ページで designMode をアクティブ化する。

Other Cheatsheets

Developer Cheat Sheets (Builder)

Assessment Cheat Sheets (Breaker)

Mobile Cheat Sheets

OpSec Cheat Sheets (Defender)

Draft Cheat Sheets