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

我有一个asp.net 4.0 IIS7.5网站,我需要使用X帧头选项保护

我还需要启用我的网站页面iframed从我的同一个域名,以及从我的Facebook应用程序。

目前我有我的网站configuration了一个网站领导:

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

当我使用Chrome浏览器或FireFox查看我的Facebook页面时,我的网站页面(正在使用我的Facebook页面进行iframed)显示正常,但在IE9下,出现错误

“此页面无法显示…”(因为X-Frame_Options限制)。

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

如果只能定义一个域,那么X-FRAME-OPTION是一个新的function,这看起来根本上是有缺陷的。

X-Frame-Options已被弃用。 来自MDN :

该function已从Web标准中删除。 尽pipe一些浏览器可能仍然支持它,但它正在被丢弃。 不要在旧的或新的项目中使用它。 使用它的页面或Web应用程序可能随时中断。

现代的select是Content-Security-Policy头,它可以通过使用frame-ancestors指令,在其他许多策略中白名单列出允许哪个URL在一个框架中托pipe你的页面。
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策略。

从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 

对于yousite.com您可以使用X-Frame-Options: deny

顺便说一下 ,现在Chrome(以及所有基于webkit的浏览器) 根本不支持 ALLOW-FROM语句。

如何不仅允许多个域的方法,而且允许dynamic域。

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

棘手的业务,但我们可以知道两个事实:

  1. 加载iframe时,只会validation第一个请求中的X-Frame-Options。 加载iframe之后,您可以在iframe中导航,并且在随后的请求中不检查标题。

  2. 而且,当加载iframe时,HTTP引用者是父级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 

在这里,我们可以dynamic地允许基于父域的域。 在这种情况下,我们确保主机在sharepoint.com结束,保持我们的网站安全点击劫持。

我很乐意听到这种方法的反馈意见。

Necromancing。
提供的答案是不完整的。

首先,如前所述,您不能添加多个允许来自主机,这不被支持。
其次,您需要从HTTP引用来dynamic提取该值,这意味着您不能将值添加到Web.config,因为它不总是相同的值。

当浏览器是Chrome浏览器(在debugging控制台上会产生一个错误,可能会快速填充控制台,或者使应用程序变慢),则需要进行浏览器检测以避免添加允许。 这也意味着您需要修改ASP.NET浏览器检测,因为它错误地将Edge标识为Chrome。

这可以通过编写一个在每个请求上运行的HTTP模块在ASP.NET中完成,该模块为每个响应添加一个http头,具体取决于请求的引用者。 对于Chrome,需要添加内容安全策略。

 // 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模块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 } 

接下来,您需要将模块添加到您的应用程序。 您可以通过重写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中的另一个是IIS6。
请注意,您需要将runAllManagedModulesForAllRequests设置为true,因为它可以正常工作。

types中的string格式为"Namespace.Class, Assembly" 。 请注意,如果您使用VB.NET而不是C#编写程序集,则VB会为每个项目创build一个默认的命名空间,因此您的string将看起来像

 "[DefaultNameSpace.Namespace].Class, Assembly" 

如果你想避免这个问题,用C#编写DLL。

不完全相同,但可以在某些情况下工作:有另一种selectALLOWALL将有效地消除限制,这可能是一个很好的testing/预生产环境

根据MDN规范 , X-Frame-Options: ALLOW-FROM在Chrome中不受支持,并且在Edge和Opera中支持未知。

Content-Security-Policy: frame-ancestors重写X-Frame-Options (按照W3规范 ),但frame-ancestors兼容性有限。 按照这些MDN规范 ,IE或Edge不支持。

一种可能的解决方法是使用这里描述的“断帧器”脚本

你只需要改变“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未启用的情况下,您将不会担心恶意网站构成您的页面。

是。 这种方法允许多个域。

VB.NET

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