X-Frame-Options Allow-From múltiples dominios

Tengo un sitio asp.net 4.0 IIS7.5 que necesito asegurar usando la opción x-frame headers

También necesito habilitar las páginas de mi sitio para que sean iframed desde mi mismo dominio y desde mi aplicación de Facebook.

Actualmente tengo mi sitio configurado con un sitio encabezado por:

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

Cuando vi mi página de Facebook con Chrome o FireFox, mis páginas de sitios (que están incluidas en mi página de Facebook) se muestran bien, pero en IE9, aparece el error

“esta página no se puede mostrar …” (debido a la restricción X-Frame_Options ).

¿Cómo configuro las X-Frame-Options: ALLOW-FROM para admitir más de un dominio?

X-FRAME-OPTION es una nueva característica que parece fundamentalmente defectuosa si solo se puede definir un único dominio.

X-Frame-Options está en desuso. De MDN :

Esta característica ha sido eliminada de los estándares web. Aunque algunos navegadores aún pueden soportarlo, está en proceso de descarte. No lo use en proyectos antiguos o nuevos. Las páginas o aplicaciones web que lo usan pueden romperse en cualquier momento.

La alternativa moderna es el Content-Security-Policy , que a lo largo de muchas otras políticas puede hacer una lista blanca de las URL permitidas para alojar su página en un marco, utilizando la directiva frame-ancestors .
frame-ancestors admite múltiples dominios e incluso comodines, por ejemplo:

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

Desafortunadamente, por ahora, Internet Explorer no es totalmente compatible con Content-Security-Policy .

ACTUALIZACIÓN: MDN ha eliminado su comentario de desaprobación. Aquí hay un comentario similar del Nivel de la Política de Seguridad de Contenido del W3C

La directiva frame-ancestors obsoleto el encabezado X-Frame-Options . Si un recurso tiene ambas políticas, DEBERÍA aplicarse la política de frame-ancestors X-Frame-Options DEBERÍA ignorarse la política de X-Frame-Options .

De RFC 7034 :

No se permiten comodines o listas para declarar varios dominios en una instrucción ALLOW-FROM

Asi que,

¿Cómo configuro las Opciones X-Frame: ALLOW-FROM para admitir más de un dominio?

No puedes. Como solución, puede usar diferentes URL para diferentes socios. Para cada URL, puede usar su propio valor de X-Frame-Options . Por ejemplo:

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

Para yousite.com puede usar X-Frame-Options: deny .

Por cierto , por ahora, Chrome (y todos los navegadores basados ​​en webkits) no son compatibles ALLOW-FROM sentencias ALLOW-FROM .

¿Qué tal un enfoque que no solo permite múltiples dominios, sino que permite dominios dynamics?

El caso de uso aquí es con una parte de la aplicación Sharepoint que carga nuestro sitio dentro de Sharepoint a través de un iframe. El problema es que sharepoint tiene subdominios dynamics como https://yoursite.sharepoint.com . Entonces, para IE, necesitamos especificar ALLOW-FROM https: //.sharepoint.com

Negocio difícil, pero podemos hacerlo sabiendo dos hechos:

  1. Cuando un iframe se carga, solo valida las X-Frame-Options en la primera solicitud. Una vez que se carga el iframe, puede navegar dentro del iframe y el encabezado no se verifica en las solicitudes posteriores.

  2. Además, cuando se carga un iframe, el referer de HTTP es la URL del iframe padre.

Puede aprovechar estos dos hechos del lado del servidor. En ruby, estoy usando el siguiente código:

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

Aquí podemos permitir dinámicamente dominios basados ​​en el dominio principal. En este caso, nos aseguramos de que el host finalice en sharepoint.com, lo que mantendrá nuestro sitio a salvo del clickjacking.

Me encantaría escuchar comentarios sobre este enfoque.

Nigromancia
Las respuestas proporcionadas son incompletas.

En primer lugar, como ya se dijo, no se pueden agregar varios hosts de permiso, eso no es compatible.
En segundo lugar, debe extraer dinámicamente ese valor de la referencia HTTP, lo que significa que no puede agregar el valor a Web.config, porque no siempre es el mismo valor.

Será necesario hacer la detección del navegador para evitar agregar permiso cuando el navegador sea Chrome (produce un error en la consola de depuración, que puede llenar rápidamente la consola o hacer que la aplicación sea lenta). Eso también significa que debe modificar la detección del navegador ASP.NET, ya que identifica erróneamente a Edge como Chrome.

Esto se puede hacer en ASP.NET escribiendo un módulo HTTP que se ejecuta en cada solicitud, que agrega un encabezado http para cada respuesta, dependiendo de la referencia de la solicitud. Para Chrome, necesita agregar 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 ls = new System.Collections.Generic.List(); 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 

Necesita registrar la función context_EndRequest en la función Init del módulo HTTP.

 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 } 

A continuación, debe agregar el módulo a su aplicación. Puede hacer esto mediante progtwigción en Global.asax anulando la función Init de HttpApplication, así:

 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) { } } } 

o puede agregar entradas a Web.config si no posee el código fuente de la aplicación:

             

La entrada en system.webServer es para IIS7 +, la otra en system.web es para IIS 6.
Tenga en cuenta que debe establecer runAllManagedModulesForAllRequests en true, para que funcione correctamente.

La cadena en tipo está en el formato "Namespace.Class, Assembly" . Tenga en cuenta que si escribe su ensamblado en VB.NET en lugar de C #, VB crea un espacio de nombres predeterminado para cada proyecto, por lo que su cadena se verá como

 "[DefaultNameSpace.Namespace].Class, Assembly" 

Si desea evitar este problema, escriba la DLL en C #.

No es exactamente lo mismo, pero podría funcionar en algunos casos: hay otra opción, ALLOWALL que eliminará efectivamente la restricción, lo que podría ser algo bueno para los entornos de prueba / preproducción.

Según las especificaciones de MDN , X-Frame-Options: ALLOW-FROM no es compatible con Chrome y el soporte es desconocido en Edge y Opera.

Content-Security-Policy: frame-ancestors reemplaza X-Frame-Options (según esta especificación W3 ), pero frame-ancestors tiene compatibilidad limitada. Según estas especificaciones de MDN , no es compatible con IE o Edge.

Una posible solución sería usar un script “frame-breaker” como se describe aquí

Solo necesita modificar la statement “if” para verificar los dominios permitidos.

  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; } 

Esta solución sería segura, creo. porque con javascript no habilitado no tendrá preocupaciones de seguridad sobre un sitio web malicioso que enmarque su página.

SÍ. Este método permitió dominio múltiple.

VB.NET

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