X-Frame-Options Allow-From multiple domains


99

X-Frame-Optionsヘッダーを使用してセキュリティで保護する必要があるASP.NET 4.0 IIS7.5サイトがあります。

また、自分のサイトのページを、同じドメインから、またFacebookのアプリからもiframe化できるようにする必要があります。

現在、私は自分のサイトを次の見出しのサイトで構成しています:

Response.Headers.Add("X-Frame-Options", "ALLOW-FROM SAMEDOMAIN, www.facebook.com/MyFBSite")

ChromeまたはFirefoxでFacebookページを表示したときに、私のFacebookページで表示されたサイトページは正常に表示されますが、IE9ではエラーが発生します。

「このページは表示できません…」(X-Frame_Options制限のため)。

X-Frame-Options: ALLOW-FROM複数のドメインをサポートするようにを設定するにはどうすればよいですか?

X-FRAME-OPTION 単一のドメインしか定義できない場合、新機能であることは根本的に欠陥があるようです。


2
これは既知の制限であるように思わ:owasp.org/index.php/...
ピエール・エルンスト

回答:


108

X-Frame-Options廃止予定です。MDNから:

この機能はWeb標準から削除されました。一部のブラウザではまだサポートされている可能性がありますが、削除される予定です。古いプロジェクトや新しいプロジェクトでは使用しないでください。それを使用しているページやWebアプリはいつでも壊れる可能性があります。

最新の代替手段はContent-Security-Policyヘッダーです。他の多くのポリシーとともに、frame-ancestorsディレクティブを使用して、フレーム内でページをホストすることを許可されるURLをホワイトリストに登録できます。
frame-ancestors複数のドメインとワイルドカードをサポートします。次に例を示します。

Content-Security-Policy: frame-ancestors 'self' example.com *.example.net ;

残念ながら、現時点では、Internet ExplorerはContent-Security-Policyを完全にはサポートしていません

更新: MDNは非推奨コメントを削除しました。これはW3Cのコンテンツセキュリティポリシーレベルからの同様のコメントです

frame-ancestorsディレクティブは廃止X-Frame-Optionsヘッダー。リソースに両方のポリシーがある場合、ポリシーはframe-ancestors強制さX-Frame-Optionsれるべきで(SHOULD)、ポリシーは無視されるべきです(SHOULD)。


14
frame-ancestorsは、MDNで「実験的なAPIであり、製品コードでは使用しないでください」とマークされています。+ X-Frame-Optionsは非推奨ではありませんが「非標準」ですが、「広くサポートされており、CSPと組み合わせて使用​​できます」
Jonathan Muller

1
@JonathanMuller-表現がX-Frame-Options変更され、現在はそれほど厳しくありません。ファイナライズされていないスペックを使用するのはリスクが高いのは良い点です。ありがとう!
Kobi

2
MDNに関する警告が表示されなくなりました。Mozillaは意見を変えましたか?
thomaskonrad 2016

2
@ to0om-ありがとうございます!別のコメントで回答を更新しました。私の答えは強すぎるかもしれません。いずれにせよ、X-Frame-Options複数のソースをサポートしていません。
Kobi

4
@コビ、私は答えを再編成する必要があると思います。最初の文は、これはMDNに従って非推奨であると述べています。更新を上部に追加すると、誤解を招きにくくなります(太字の "UPDATE:"が付いています)。ありがとう。
Kasun Gajasinghe

39

RFC 7034から:

1つのALLOW-FROMステートメントで複数のドメインを宣言するためのワイルドカードまたはリストは許可されていません

そう、

X-Frame-Options:ALLOW-FROMを設定して複数のドメインをサポートするにはどうすればよいですか?

できません。回避策として、パートナーごとに異なるURLを使用できます。URLごとに、独自のX-Frame-Options値を使用できます。例えば:

partner   iframe URL       ALLOW-FROM
---------------------------------------
Facebook  fb.yoursite.com  facebook.com
VK.COM    vk.yoursite.com  vk.com

以下のためにyousite.comあなただけ使用することができますX-Frame-Options: deny

ところで、今のところChrome(およびすべてのWebkitベースのブラウザ) ALLOW-FROMステートメントをまったくサポートしていません


1
ALLOW-FROM提供されたリンクの使用がWebkitでサポートされるようです。
ジミ

3
@Jimiいいえ、そうではありません-問題のリンクの最後のコメントでは、代わりにCSPポリシーを使用する必要があると述べています。このオプションはChromeではまだ機能しません。
NickG

9

ネクロマンシング。
提供された回答は不完全です。

最初に、すでに述べたように、サポートされていない複数の許可元ホストを追加することはできません。
次に、その値をHTTPリファラーから動的に抽出する必要があります。つまり、常に同じ値であるとは限らないため、値をWeb.configに追加できません。

ブラウザーがChromeの場合、許可を追加しないようにブラウザー検出を行う必要があります(デバッグ-コンソールでエラーが発生し、コンソールがすぐにいっぱいになるか、アプリケーションが遅くなります)。また、EdgeをChromeとして誤って識別するため、ASP.NETブラウザー検出を変更する必要があることも意味します。

これは、ASP.NETで、すべての要求で実行されるHTTPモジュールを記述し、要求のリファラーに応じて、すべての応答にhttpヘッダーを追加することで実行できます。Chromeの場合、Content-Security-Policyを追加する必要があります。

// /programming/31870789/check-whether-browser-is-chrome-or-edge
public class BrowserInfo
{

    public System.Web.HttpBrowserCapabilities Browser { get; set; }
    public string Name { get; set; }
    public string Version { get; set; }
    public string Platform { get; set; }
    public bool IsMobileDevice { get; set; }
    public string MobileBrand { get; set; }
    public string MobileModel { get; set; }


    public BrowserInfo(System.Web.HttpRequest request)
    {
        if (request.Browser != null)
        {
            if (request.UserAgent.Contains("Edge")
                && request.Browser.Browser != "Edge")
            {
                this.Name = "Edge";
            }
            else
            {
                this.Name = request.Browser.Browser;
                this.Version = request.Browser.MajorVersion.ToString();
            }
            this.Browser = request.Browser;
            this.Platform = request.Browser.Platform;
            this.IsMobileDevice = request.Browser.IsMobileDevice;
            if (IsMobileDevice)
            {
                this.Name = request.Browser.Browser;
            }
        }
    }


}


void context_EndRequest(object sender, System.EventArgs e)
{
    if (System.Web.HttpContext.Current != null && System.Web.HttpContext.Current.Response != null)
    {
        System.Web.HttpResponse response = System.Web.HttpContext.Current.Response;

        try
        {
            // response.Headers["P3P"] = "CP=\\\"IDC DSP COR ADM DEVi TAIi PSA PSD IVAi IVDi CONi HIS OUR IND CNT\\\"":
            // response.Headers.Set("P3P", "CP=\\\"IDC DSP COR ADM DEVi TAIi PSA PSD IVAi IVDi CONi HIS OUR IND CNT\\\"");
            // response.AddHeader("P3P", "CP=\\\"IDC DSP COR ADM DEVi TAIi PSA PSD IVAi IVDi CONi HIS OUR IND CNT\\\"");
            response.AppendHeader("P3P", "CP=\\\"IDC DSP COR ADM DEVi TAIi PSA PSD IVAi IVDi CONi HIS OUR IND CNT\\\"");

            // response.AppendHeader("X-Frame-Options", "DENY");
            // response.AppendHeader("X-Frame-Options", "SAMEORIGIN");
            // response.AppendHeader("X-Frame-Options", "AllowAll");

            if (System.Web.HttpContext.Current.Request.UrlReferrer != null)
            {
                // "X-Frame-Options": "ALLOW-FROM " Not recognized in Chrome 
                string host = System.Web.HttpContext.Current.Request.UrlReferrer.Scheme + System.Uri.SchemeDelimiter
                            + System.Web.HttpContext.Current.Request.UrlReferrer.Authority
                ;

                string selfAuth = System.Web.HttpContext.Current.Request.Url.Authority;
                string refAuth = System.Web.HttpContext.Current.Request.UrlReferrer.Authority;

                // SQL.Log(System.Web.HttpContext.Current.Request.RawUrl, System.Web.HttpContext.Current.Request.UrlReferrer.OriginalString, refAuth);

                if (IsHostAllowed(refAuth))
                {
                    BrowserInfo bi = new BrowserInfo(System.Web.HttpContext.Current.Request);

                    // bi.Name = Firefox
                    // bi.Name = InternetExplorer
                    // bi.Name = Chrome

                    // Chrome wants entire path... 
                    if (!System.StringComparer.OrdinalIgnoreCase.Equals(bi.Name, "Chrome"))
                        response.AppendHeader("X-Frame-Options", "ALLOW-FROM " + host);    

                    // unsafe-eval: invalid JSON https://github.com/keen/keen-js/issues/394
                    // unsafe-inline: styles
                    // data: url(data:image/png:...)

                    // https://www.owasp.org/index.php/Clickjacking_Defense_Cheat_Sheet
                    // https://www.ietf.org/rfc/rfc7034.txt
                    // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Frame-Options
                    // https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP

                    // /programming/10205192/x-frame-options-allow-from-multiple-domains
                    // https://content-security-policy.com/
                    // http://rehansaeed.com/content-security-policy-for-asp-net-mvc/

                    // This is for Chrome:
                    // response.AppendHeader("Content-Security-Policy", "default-src 'self' 'unsafe-inline' 'unsafe-eval' data: *.msecnd.net vortex.data.microsoft.com " + selfAuth + " " + refAuth);


                    System.Collections.Generic.List<string> ls = new System.Collections.Generic.List<string>();
                    ls.Add("default-src");
                    ls.Add("'self'");
                    ls.Add("'unsafe-inline'");
                    ls.Add("'unsafe-eval'");
                    ls.Add("data:");

                    // http://az416426.vo.msecnd.net/scripts/a/ai.0.js

                    // ls.Add("*.msecnd.net");
                    // ls.Add("vortex.data.microsoft.com");

                    ls.Add(selfAuth);
                    ls.Add(refAuth);

                    string contentSecurityPolicy = string.Join(" ", ls.ToArray());
                    response.AppendHeader("Content-Security-Policy", contentSecurityPolicy);
                }
                else
                {
                    response.AppendHeader("X-Frame-Options", "SAMEORIGIN");
                }

            }
            else
                response.AppendHeader("X-Frame-Options", "SAMEORIGIN");
        }
        catch (System.Exception ex)
        {
            // WTF ? 
            System.Console.WriteLine(ex.Message); // Suppress warning
        }

    } // End if (System.Web.HttpContext.Current != null && System.Web.HttpContext.Current.Response != null)

} // End Using context_EndRequest


private static string[] s_allowedHosts = new string[] 
{
     "localhost:49533"
    ,"localhost:52257"
    ,"vmcompany1"
    ,"vmcompany2"
    ,"vmpostalservices"
    ,"example.com"
};


public static bool IsHostAllowed(string host)
{
    return Contains(s_allowedHosts, host);
} // End Function IsHostAllowed 


public static bool Contains(string[] allowed, string current)
{
    for (int i = 0; i < allowed.Length; ++i)
    {
        if (System.StringComparer.OrdinalIgnoreCase.Equals(allowed[i], current))
            return true;
    } // Next i 

    return false;
} // End Function Contains 

HTTPモジュールのInit関数にcontext_EndRequest関数を登録する必要があります。

public class RequestLanguageChanger : System.Web.IHttpModule
{


    void System.Web.IHttpModule.Dispose()
    {
        // throw new NotImplementedException();
    }


    void System.Web.IHttpModule.Init(System.Web.HttpApplication context)
    {
        // /programming/441421/httpmodule-event-execution-order
        context.EndRequest += new System.EventHandler(context_EndRequest);
    }

    // context_EndRequest Code from above comes here


}

次に、モジュールをアプリケーションに追加する必要があります。次のように、HttpApplicationのInit関数をオーバーライドすることで、Global.asaxでプログラム的にこれを行うことができます。

namespace ChangeRequestLanguage
{


    public class Global : System.Web.HttpApplication
    {

        System.Web.IHttpModule mod = new libRequestLanguageChanger.RequestLanguageChanger();

        public override void Init()
        {
            mod.Init(this);
            base.Init();
        }



        protected void Application_Start(object sender, System.EventArgs e)
        {

        }

        protected void Session_Start(object sender, System.EventArgs e)
        {

        }

        protected void Application_BeginRequest(object sender, System.EventArgs e)
        {

        }

        protected void Application_AuthenticateRequest(object sender, System.EventArgs e)
        {

        }

        protected void Application_Error(object sender, System.EventArgs e)
        {

        }

        protected void Session_End(object sender, System.EventArgs e)
        {

        }

        protected void Application_End(object sender, System.EventArgs e)
        {

        }


    }


}

または、アプリケーションのソースコードを所有していない場合は、Web.configにエントリを追加できます。

      <httpModules>
        <add name="RequestLanguageChanger" type= "libRequestLanguageChanger.RequestLanguageChanger, libRequestLanguageChanger" />
      </httpModules>
    </system.web>

  <system.webServer>
    <validation validateIntegratedModeConfiguration="false"/>

    <modules runAllManagedModulesForAllRequests="true">
      <add name="RequestLanguageChanger" type="libRequestLanguageChanger.RequestLanguageChanger, libRequestLanguageChanger" />
    </modules>
  </system.webServer>
</configuration>

system.webServerのエントリはIIS7 +用で、system.webのもう1つはIIS 6用です。
のもう1つ用です。正しく機能するためには、runAllManagedModulesForAllRequestsをtrueに設定する必要があります。

typeの文字列は次の形式です"Namespace.Class, Assembly"。C#ではなくVB.NETでアセンブリを作成すると、VBはプロジェクトごとにデフォルトの名前空間を作成するため、文字列は次のようになります。

"[DefaultNameSpace.Namespace].Class, Assembly"

この問題を回避するには、DLLをC#で記述します。


「vmswisslife」と「vmraiffeisen」を回答から削除して、誤った相関関係が得られないようにするとよいでしょう。
ケツァルコアトル

@quetzalcoatl:私はそれらを例として残しました、それは見落としではなく、決して機密情報ではありません。しかし、本当は、おそらくそれらを削除する方が良いでしょう。できました。
Stefan Steiger

7

複数のドメインを許可するだけでなく、動的ドメインを許可するアプローチについてはどうでしょう。

ここでの使用例は、Sharepoint内にiframeを介してサイトを読み込むSharepointアプリパーツでの使用です。問題は、Sharepointにhttps://yoursite.sharepoint.comなどの動的サブドメインがあることですです。IEの場合、ALLOW-FROM https://.sharepoint.comを指定する必要があります

トリッキーなビジネスですが、次の2つの事実を知っていればそれを成し遂げることができます。

  1. iframeが読み込まれると、最初のリクエストでX-Frame-Optionsのみが検証されます。iframeが読み込まれると、iframe内をナビゲートでき、ヘッダーは後続のリクエストでチェックされません。

  2. また、iframeが読み込まれると、HTTPリファラーが親iframeのURLになります。

これら2つのファクトサーバー側を活用できます。Rubyでは、次のコードを使用しています。

  uri = URI.parse(request.referer)
  if uri.host.match(/\.sharepoint\.com$/)
    url = "https://#{uri.host}"
    response.headers['X-Frame-Options'] = "ALLOW-FROM #{url}"
  end

ここでは、親ドメインに基づいてドメインを動的に許可できます。この場合、ホストがsharepoint.comで終わり、サイトがクリックジャッキングから保護されていることを確認します。

このアプローチについてのフィードバックをお待ちしています。


2
注意:ホストが「fakesharepoint.com」の場合、これは機能しません。正規表現は次のようになります/\.sharepoint\.com$/
nitsas

@StefanSteigerは正しいですが、Chromeでもこの問題は発生しません。Chromeなどの標準に準拠したブラウザは、新しいコンテンツセキュリティポリシー(CSP)モデルに準拠しています。
Peter P.

4

MDN仕様に従って、X-Frame-Options: ALLOW-FROMはChromeではサポートされておらず、サポートはEdgeおよびOperaでは不明です。

Content-Security-Policy: frame-ancestorsX-Frame-OptionsこのW3仕様に従って)オーバーライドしますが、frame-ancestors互換性に制限があります。これらのMDN仕様に従って、IEまたはEdgeではサポートされていません。


1

HTTPヘッダーフィールドX-Frame-OptionsのRFCには、X-Frame-Optionsヘッダー値の「ALLOW-FROM」フィールドに含めることができるドメインは1つだけであると記載されています。複数のドメインは許可されていません。

RFCは、この問題の回避策を提案しています。解決策は、ドメイン名をiframe src urlのurlパラメーターとして指定することです。iframe src urlをホストするサーバーは、urlパラメータで指定されたドメイン名を確認できます。ドメイン名が有効なドメイン名のリストと一致する場合、サーバーはX-Frame-Optionsヘッダーに値 "ALLOW-FROM domain-name"を送信できます。ここで、ドメイン名は、試行しているドメインの名前です。リモートコンテンツを埋め込みます。ドメイン名が指定されていないか無効な場合は、X-Frame-Optionsヘッダーを値「deny」で送信できます。


1

厳密にはいいえ、できません。

ただし指定することができX-Frame-Options: mysite.com、したがって、許可subdomain1.mysite.comsubdomain2.mysite.com。しかし、はい、それはまだ1つのドメインです。これにはいくつかの回避策がありますが、RFC仕様で直接読むのが最も簡単だと思います:https : //tools.ietf.org/html/rfc7034

Content-Security-Policy(CSP)ヘッダーのframe-ancestorディレクティブがX-Frame-Optionsを廃止することも指摘する価値があります。詳細はこちら


0

まったく同じではありませんが、場合によってALLOWALLは機能する可能性があります。制限を効果的に削除する別のオプションがあります。これは、テスト/実稼働前の環境に適しています。


これはMDNで文書化されていません。
andig

0

IEのX-Frame-Optionsと他のブラウザーのContent-Security-Policyを追加する必要がありました。だから私は次のようなことをしました。

if allowed_domains.present?
  request_host = URI.parse(request.referer)
  _domain = allowed_domains.split(" ").include?(request_host.host) ? "#{request_host.scheme}://#{request_host.host}" : app_host
  response.headers['Content-Security-Policy'] = "frame-ancestors #{_domain}"
  response.headers['X-Frame-Options'] = "ALLOW-FROM #{_domain}"
else
  response.headers.except! 'X-Frame-Options'
end

-4

1つの可能な回避策は、ここで説明さている「フレームブレーカー」スクリプトを使用することです。

許可されたドメインを確認するには、「if」ステートメントを変更するだけです。

   if (self === top) {
       var antiClickjack = document.getElementById("antiClickjack");
       antiClickjack.parentNode.removeChild(antiClickjack);
   } else {
       //your domain check goes here
       if(top.location.host != "allowed.domain1.com" && top.location.host == "allowed.domain2.com")
         top.location = self.location;
   }

この回避策は安全だと思います。JavaScriptが有効になっていないと、悪意のあるWebサイトがページをフレーミングするというセキュリティ上の懸念がないためです。


1
これは、top.locationを呼び出すときに同じオリジンポリシーが原因で機能しません。
エリックR. 14年

-8

はい。この方法では、複数のドメインを使用できました。

VB.NET

response.headers.add("X-Frame-Options", "ALLOW-FROM " & request.urlreferer.tostring())

9
これは、X-Frame-Optionsの目的に反するようです。これにより、どのサイトでもフレームを作成できるようになります。
Andrey Shchekin

5
この答えはソリューションとしては良いベースのようですが、request.urlreferer.tostring()が許可したいオリジンの1つである場合にのみこのコードを実行するように、追加のロジックが必要です。
Zergleb、2015年

これを行っている場合、なぜX-Frame-Optionsヘッダーを使用しているのか...無視してください
vs4vijay
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.