2012-04-18 17 views
54

Tengo un 4.0 IIS7.5 sitio asp.net que necesito asegura usando la opción cabeceras fotograma xX-Frame-opciones permiten-De múltiples dominios

que también tienen que permitir que mis páginas del sitio para ser iframes desde mi mismo dominio y desde mi aplicación de Facebook.

Actualmente tengo mi sitio configurado con un sitio de encabezado de:

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 los sitios (siendo iframe con mi página de facebook) son display, pero bajo IE9, obtengo el error

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

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

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

+2

Esto parece ser una limitación conocida: https://www.owasp.org/index.php/Clickjacking_Defense_Cheat_Sheet#Limitations –

Respuesta

23

De RFC 7034:

comodines o listas para declarar múltiples dominios en un solo ALLOW-FROM no están permitidos

Así,

¿Cómo se configura el X- Frame-Options: ALLOW-FROM para soportar más de un dominio individual?

No puede. 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 sólo puede utilizar X-Frame-Options: deny.

Por cierto, por ahora Chrome (y todos los navegadores basados ​​en WebKit) does not supportALLOW-FROM declaraciones en absoluto.

+1

Parece que ahora es compatible con 'webkit-PERMITIR FROM' utilizando el enlace que ya ha proporcionado. – Jimi

+0

@Jimi No, no. El último comentario en el enlace en cuestión dice que, en su lugar, debe usar una política de CSP. Esta opción aún no funciona en Chrome. – NickG

-2

Una posible solución sería usar una escritura "marco automático" como se describe here

Sólo tiene que alterar el "if" para comprobar si sus 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 estaría a salvo, creo. porque con javascript no habilitado no tendrá preocupaciones de seguridad sobre un sitio web malicioso que enmarque su página.

+1

Esto no funcionará debido a la misma política de origen al llamar a top.location. –

50

X-Frame-Options está obsoleto. De MDN:

Esta característica se ha eliminado 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 la cabecera Content-Security-Policy, que a lo largo de muchas otras políticas puede lista blanca lo URL que pueden alojar su página en un marco, usando la directiva frame-ancestors.
frame-ancestors soporta múltiples dominios e incluso comodines, por ejemplo:

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

Desafortunadamente, por ahora, Internet Explorer does not fully support Content-Security-Policy.

ACTUALIZACIÓN: MDN ha eliminado su comentario de desaprobación. He aquí un comentario similar de W3C's Content Security Policy Level

El frame-ancestors Directiva obsoletes la cabecera X-Frame-Options. Si un recurso tiene ambas políticas, la política frame-ancestors DEBERÍA aplicarse y la política X-Frame-Options DEBERÍA ignorarse.

+12

frame-ancestors está marcado como "API experimental y no debe usarse en código de producción" en MDN. + X-Frame-Options no está en desuso, pero "no estándar" pero "es ampliamente compatible y se puede usar junto con CSP" –

+1

@JonathanMuller - La fraseología en 'X-Frame-Options' cambió, y es menos severa ahora.Es un buen punto que es arriesgado usar una especificación que no está finalizada. ¡Gracias! – Kobi

+2

Ya no puedo encontrar la advertencia obsoleta en MDN. ¿Mozilla ha cambiado su opinión? – thomaskonrad

-3

YES. Este método permitió dominio múltiple.

VB.NET

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

Esto parece frustrar el propósito de X-Frame-Options, ya que permite que cualquier sitio encuadre. –

+4

Esta respuesta parece que podría ser una buena base como solución, pero necesita una lógica adicional para que solo ejecute este código si request.urlreferer.tostring() es uno de los orígenes que desea permitir. – Zergleb

+0

Si está haciendo esto, ¿por qué está usando el encabezado X-Frame-Options ... simplemente ignórelo – vs4vijay

5

¿Qué tal un enfoque que no sólo permite múltiples dominios, pero permite dominios dinámicos.

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 dinámicos como https://yoursite.sharepoint.com. Así que para IE, tenemos que especificar PERMITIR-DE https: //.sharepoint.com

negocio difícil, pero podemos lograr que se haga saber dos hechos:

  1. Cuando un iframe cargas, sólo valida las opciones X-Frame 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 HTTP es la URL del iframe principal.

Puede aprovechar estos dos aspectos 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 recibir comentarios sobre este enfoque.

+1

Precaución: esto se rompe si el host es "fakesharepoint.com". La expresión regular debería ser: '/ \. Sharepoint \ .com $ /' – nitsas

+0

Gracias @nitsas he actualizado anteriormente –

+0

Eso no funcionará para Chrome, ya que no admite PERMITIR-DESDE ... –

0

No es exactamente la misma, pero podría funcionar para algunos casos: no hay otra opción ALLOWALL que eliminará de manera efectiva la restricción, lo que podría ser una cosa agradable para entornos de prueba/pre-producción

4

Necromancing.
Las respuestas proporcionadas están incompletas.

En primer lugar, como ya se dijo, no puede agregar múltiples 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<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 

Debe 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. Usted puede hacer esto mediante programación en Global.asax anulando la función Init del 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 se puede añadir entradas a Web.config si usted no posee el código fuente de la aplicación :

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

la entrada en system.webServer es para IIS7 +, y el otro en system.web es para IIS 6.
Tenga en cuenta que es necesario establecer runAllManagedModulesForAllRequests true, para que funcione correctamente.

La cadena en tipo tiene el formato "Namespace.Class, Assembly". Tenga en cuenta que si usted escribe su montaje en VB.NET en lugar de C#, Visual Basic crea un espacio de nombres por defecto, para cada proyecto, por lo que la cadena se verá como

"[DefaultNameSpace.Namespace].Class, Assembly" 

Si se quiere evitar este problema, escriba la DLL Cª#.

0

Según MDN Specifications, X-Frame-Options: ALLOW-FROM no es compatible con Chrome y se desconoce el soporte en Edge y Opera.

Content-Security-Policy: frame-ancestors reemplaza X-Frame-Options (según this W3 spec), pero frame-ancestors tiene compatibilidad limitada. Según estos MDN Specs, no es compatible con IE o Edge.

Cuestiones relacionadas