X-Frame-Options允许来自多个域

use*_*663 89 asp.net security iis-7 header internet-explorer-9

我有一个asp.net 4.0 IIS7.5站点,我需要使用x-frame headers选项进行保护

我还需要启用我的网站页面来自我的同一个域以及我的Facebook应用程序.

目前,我的网站配置了以下网站:

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

当我用Chrome或FireFox查看我的Facebook页面时,我的网站页面(与我的Facebook页面一起被设置)显示正常,但在IE9下,我收到错误

"此页面无法显示......"(因为X-Frame_Options限制).

如何设置X-Frame-Options: ALLOW-FROM支持多个域?

X-FRAME-OPTION 如果只能定义一个域,那么作为一个新功能似乎存在根本缺陷.

Kob*_*obi 92

X-Frame-Options已弃用.来自MDN:

此功能已从Web标准中删除.虽然有些浏览器可能仍然支持它,但它正在被删除.不要在旧项目或新项目中使用它.使用它的页面或Web应用程序可能随时中断.

现代替代方案是Content-Security-Policy标题,其中许多其他策略可以使用该frame-ancestors指令以白名单列出允许在框架中托管页面的URL .
frame-ancestors支持多个域甚至通配符,例如:

Content-Security-Policy: frame-ancestors 'self' example.com *.example.net ;
Run Code Online (Sandbox Code Playgroud)

不幸的是,目前,Internet Explorer并不完全支持Content-Security-Policy.

更新: MDN已删除其弃用评论.以下是W3C内容安全策略级别的类似评论

frame-ancestors指令废弃X-Frame-Options标题.如果资源同时具有两个策略,则frame-ancestors应该强制执行该X-Frame-Options策略,并且应该忽略该策略.

  • frame-ancestors在MDN上标记为"实验API,不应在生产代码中使用".+ X-Frame-Options不被弃用,但是"非标准",但"广泛支持并且可以与CSP一起使用" (14认同)
  • @Kobi,我认为答案需要重新组织.第一句话说这是根据MDN弃用的.如果您在顶部添加更新(使用粗体颜色"UPDATE:"),则会减少误导性.谢谢. (4认同)
  • 我再也找不到MDN上的去除警告了.Mozilla有没有改变他们的意见? (2认同)
  • @ to0om - 谢谢!我用另一条评论更新了答案.我的回答可能过于强烈了.无论哪种方式,`X-Frame-Options`都不支持多个来源. (2认同)

vbo*_*vbo 36

来自RFC 7034:

不允许在一个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
Run Code Online (Sandbox Code Playgroud)

因为yousite.com你可以使用X-Frame-Options: deny.

BTW,目前Chrome(以及所有基于webkit的浏览器)根本不支持 ALLOW-FROM语句.

  • @Jimi不,它没有 - 有关链接的最后评论,说你需要使用CSP政策.此选项在Chrome中仍然无效. (3认同)

Pet*_* P. 7

一种方法如何不仅允许多个域,而且允许动态域.

这里的用例是一个Sharepoint应用程序部分,它通过iframe将我们的站点加载到Sharepoint内部.问题是sharepoint有动态子域名,例如https://yoursite.sharepoint.com.所以对于IE,我们需要指定ALLOW-FROM https://.sharepoint.com

棘手的业务,但我们可以通过两个事实来完成它:

  1. 加载iframe时,它仅在第一个请求中验证X-Frame-Options.加载iframe后,您可以在iframe中导航,并且不会在后续请求中检查标头.

  2. 此外,当加载iframe时,HTTP referer是父iframe url.

您可以利用服务器端的这两个事实.在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
Run Code Online (Sandbox Code Playgroud)

在这里,我们可以动态地允许基于父域的域.在这种情况下,我们确保主机在sharepoint.com中结束,以确保我们的站点免受点击劫持.

我很想听听有关这种方法的反馈.

  • 注意:如果主机是"fakesharepoint.com",则会中断.正则表达式应该是:`/\.Sharepoint\.com $ /` (2认同)

Ste*_*ger 7

Necromancing.
提供的答案不完整.

首先,如前所述,您无法添加多个allow-from主机,这是不受支持的.
其次,您需要从HTTP引用者动态提取该值,这意味着您无法将值添加到Web.config,因为它并不总是相同的值.

有必要进行浏览器检测以避免在浏览器为Chrome时添加allow-from(它会在调试控制台上产生错误,这可能会快速填满控制台,或者使应用程序变慢).这也意味着您需要修改ASP.NET浏览器检测,因为它错误地将Edge标识为Chrome.

这可以通过编写一个在每个请求上运行的HTTP模块在ASP.NET中完成,该模块为每个响应附加一个http-header,具体取决于请求的referrer.对于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 
Run Code Online (Sandbox Code Playgroud)

您需要在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)
    {
        // https://stackoverflow.com/questions/441421/httpmodule-event-execution-order
        context.EndRequest += new System.EventHandler(context_EndRequest);
    }

    // context_EndRequest Code from above comes here


}
Run Code Online (Sandbox Code Playgroud)

接下来,您需要将模块添加到您的应用程序中.您可以通过重写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)
        {

        }


    }


}
Run Code Online (Sandbox Code Playgroud)

或者,如果您不拥有应用程序源代码,则可以向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>
Run Code Online (Sandbox Code Playgroud)

system.webServer中的条目用于IIS7 +,system.web中的另一个条目用于IIS 6.
请注意,您需要将runAllManagedModulesForAllRequests设置为true,因为它可以正常工作.

类型中的字符串采用格式"Namespace.Class, Assembly".请注意,如果您使用VB.NET而不是C#编写程序集,VB会为每个项目创建一个default-Namespace,因此您的字符串看起来像

"[DefaultNameSpace.Namespace].Class, Assembly"
Run Code Online (Sandbox Code Playgroud)

如果要避免此问题,请在C#中编写DLL.


归档时间:

查看次数:

135799 次

最近记录:

5 年,11 月 前