web-dev-qa-db-ja.com

X-Frame-Options Allow-From複数ドメイン

X-frame headersオプションを使用して保護する必要があるasp.net 4.0 IIS7.5サイトがあります

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

現在、次のサイトにサイトを設定しています:

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

ChromeまたはFireFoxでFacebookページを表示すると、サイトページ(facebookページでiframingされている)は正常に表示されますが、IE9ではエラーが発生します

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

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

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

79
user1340663

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ポリシーを適用する必要があり(SHOULD)、X-Frame-Optionsポリシーを無視する必要があります。

85
Kobi

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を使用できます。

BTW、現時点ではChrome(およびすべてのWebkitベースのブラウザ) サポートしていませんALLOW-FROMステートメント。

35
vbo

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

ここでの使用例は、iframeを介してSharepoint内にサイトをロードする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で終了し、クリックジャックからサイトを安全に保ちます。

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

7
Peter P.

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

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

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

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

// https://stackoverflow.com/questions/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

                    // https://stackoverflow.com/questions/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"
    ,"vmswisslife"
    ,"vmraiffeisen"
    ,"vmpost"
    ,"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モジュールの初期化関数に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)
    {
        // https://stackoverflow.com/questions/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のエントリはIIS 6用です。
runAllManagedModulesForAllRequestsをtrueに設定する必要があることに注意してください。正しく機能するためです。

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

"[DefaultNameSpace.Namespace].Class, Assembly"

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

7
Stefan Steiger

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

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

2
Andrew

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
0
jbmyid

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

0
Willyfrog