2012-04-18 15 views
54

Ho un asp.net 4.0 IIS7.5 sito che ho bisogno fissato con l'opzione di intestazioni x-frameX-Frame-Options allow-Da più domini

Ho anche bisogno di abilitare le mie pagine del sito per essere iframed dal mio stesso dominio e dalla mia app di Facebook.

Attualmente ho il mio sito configurato con un sito di testa di:

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

Quando ho visto la mia pagina di Facebook con Chrome o Firefox mie pagine pagine (in fase di iframe con la mia pagina facebook) sono visualizzazione ok, ma sotto IE9, ho ricevuto l'errore

"questa pagina non può essere visualizzata ..." (a causa della limitazione X-Frame_Options).

Come si imposta X-Frame-Options: ALLOW-FROM per supportare più di un singolo dominio?

X-FRAME-OPTION essendo una nuova funzionalità sembra fondamentalmente difettosa se è possibile definire un solo dominio.

+2

Questa sembra essere una limitazione nota: https://www.owasp.org/index.php/Clickjacking_Defense_Cheat_Sheet#Limitations –

risposta

23

Da RFC 7034:

jolly o liste di dichiarare più domini in un unico allow-FROM non è consentito

Quindi,

Come faccio a impostare il X- Frame-Options: ALLOW-FROM per supportare più di un singolo dominio?

Non è possibile. Come soluzione alternativa puoi utilizzare URL diversi per partner diversi. Per ogni URL è possibile utilizzare il proprio valore . Per esempio:

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

Per yousite.com si può semplicemente utilizzare X-Frame-Options: deny.

BTW, per ora Chrome (e tutti i browser basati su WebKit) does not supportALLOW-FROM dichiarazioni a tutti.

+1

Sembra che il webkit ora supporti 'ALLOW-FROM' utilizzando il collegamento fornito. – Jimi

+0

@Jimi No, non è vero - l'ultimo commento sul link in questione, dice che è necessario utilizzare invece un criterio CSP. Questa opzione non funziona ancora in Chrome. – NickG

-2

Una possibile soluzione sarebbe utilizzando uno script di "frame-breaker", come descritto here

Hai solo bisogno di alterare la "if" per verificare i domini consentiti.

Questa soluzione sarebbe sicura, penso. perché con javascript non abilitato non avrai preoccupazioni per la sicurezza di un sito Web dannoso che inquadra la tua pagina.

+1

Questo non funzionerà a causa della stessa politica di origine quando si chiama top.location. Gli antenati del frame –

50

X-Frame-Options è obsoleto. Da MDN:

Questa funzione è stata rimossa dagli standard Web. Sebbene alcuni browser possano ancora supportarlo, è in fase di rilascio. Non usarlo in progetti vecchi o nuovi.Le pagine o le app Web che lo utilizzano potrebbero interrompersi in qualsiasi momento.

L'alternativa moderna è il Content-Security-Policy intestazione, che insieme molte altre politiche possono white-list quali URL sono autorizzati a ospitare la vostra pagina in un frame, usando la direttiva frame-ancestors.
frame-ancestors supporta più domini e anche caratteri jolly, ad esempio:

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

Purtroppo, per ora, Internet Explorer does not fully support Content-Security-Policy.

AGGIORNAMENTO: MDN ha rimosso il commento di ritiro. Ecco un commento simile da W3C's Content Security Policy Level

La direttiva frame-ancestors obsoletes l'intestazione X-Frame-Options. Se una risorsa ha entrambi i criteri, la politica frame-ancestors DOVREBBE essere applicata e la politica X-Frame-Options DOVREBBE essere ignorata.

+12

sono contrassegnati come "API sperimentale e non devono essere utilizzati nel codice di produzione" su MDN. + X-Frame-Options non è deprecato ma "non standard" ma "è ampiamente supportato e può essere utilizzato in congiunzione con CSP" –

+1

@JonathanMuller - Il testo su "X-Frame-Options" è cambiato ed è meno severo adesso.È positivo che sia rischioso utilizzare una specifica non finalizzata. Grazie! – Kobi

+2

Non riesco più a trovare l'avviso deprecato su MDN. Mozilla ha cambiato la sua opinione? – thomaskonrad

-3

SI. Questo metodo consentiva più domini.

VB.NET

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

Questo sembra vanificare lo scopo di X-Frame-Options in quanto consente a qualsiasi sito di incorniciare. –

+4

Questa risposta sembra che potrebbe essere una buona base come soluzione ma ha bisogno di logica aggiuntiva in modo che esegua questo codice solo se request.urlreferer.tostring() è una delle origini che desideri consentire. – Zergleb

+0

Se stai facendo questo, perché stai usando anche X-Frame-Options Header ... semplicemente ignoralo – vs4vijay

5

Come su un approccio che consente non solo più domini, ma permette domini dinamici.

Il caso d'uso qui è con una parte dell'app Sharepoint che carica il nostro sito all'interno di Sharepoint tramite un iframe. Il problema è che sharepoint ha sottodomini dinamici come https://yoursite.sharepoint.com. Così per IE, dobbiamo specificare allow-DA https: //.sharepoint.com

affare complicato, ma siamo in grado di avere fatto conoscere due fatti:

  1. Quando un iframe carichi, solo convalida le X-Frame-Options alla prima richiesta. Una volta caricato l'iframe, è possibile navigare all'interno dell'iframe e l'intestazione non viene verificata nelle richieste successive.

  2. Inoltre, quando viene caricato un iframe, il referer HTTP è l'url iframe padre.

È possibile sfruttare questi due aspetti sul lato server. In ruby, sto usando il seguente codice:

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

Qui possiamo consentire dinamicamente domini basati sul dominio genitore. In questo caso, ci assicuriamo che l'host termini in sharepoint.com mantenendo il nostro sito al sicuro da clickjacking.

Mi piacerebbe ricevere feedback su questo approccio.

+1

Attenzione: questo si interrompe se l'host è "fakesharepoint.com". La regex dovrebbe essere: '/ \. Sharepoint \ .com $ /' – nitsas

+0

grazie @nitsas ho aggiornato sopra –

+0

Che non funzionerà per chrome, in quanto non supporta ALLOW-FROM ... –

0

Non esattamente lo stesso, ma potrebbe funzionare per alcuni casi: c'è un'altra opzione ALLOWALL che rimuovere efficacemente la restrizione, che potrebbe essere una cosa piacevole per gli ambienti di test/pre-produzione

4

Necromancing.
Le risposte fornite sono incomplete.

Innanzitutto, come già detto, non è possibile aggiungere più host di autorizzazione, che non è supportato.
In secondo luogo, è necessario estrapolare dinamicamente tale valore dal referrer HTTP, il che significa che non è possibile aggiungere il valore a Web.config, poiché non è sempre lo stesso valore.

Sarà necessario eseguire il rilevamento del browser per evitare l'aggiunta di allow-da quando il browser è Chrome (produce un errore sulla console di debug, che può riempire rapidamente la console o rallentare l'applicazione). Ciò significa anche che è necessario modificare il rilevamento del browser ASP.NET poiché identifica erroneamente Edge come Chrome.

Questo può essere fatto in ASP.NET scrivendo un modulo HTTP che viene eseguito su ogni richiesta, che aggiunge un'intestazione http per ogni risposta, a seconda del referrer della richiesta. Per Chrome, è necessario aggiungere 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 

È necessario registrare la funzione context_EndRequest nella funzione Init del modulo 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 


} 

Successivamente è necessario aggiungere il modulo all'applicazione. È possibile farlo a livello di codice in Global.asax sovrascrivendo la funzione Init del HttpApplication, come questo:

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

     } 


    } 


} 

oppure è possibile aggiungere voci al Web.config se non si possiede il codice sorgente dell'applicazione :

 <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 voce nel system.webServer è per IIS7 +, l'altro in system.web è per IIS 6.
si noti che è necessario impostare runAllManagedModulesForAllRequests su true, per il corretto funzionamento.

La stringa in tipo è nel formato "Namespace.Class, Assembly". Si noti che se si scrive il montaggio in VB.NET invece di C#, VB crea un default-Namespace per ogni progetto, in modo dalla stringa sarà simile

"[DefaultNameSpace.Namespace].Class, Assembly" 

Se si vuole evitare questo problema, prego scrivere la DLL in C#.

0

Come da MDN Specifications, X-Frame-Options: ALLOW-FROM non è supportato in Chrome e il supporto è sconosciuto in Edge e Opera.

Content-Security-Policy: frame-ancestors sovrascrive X-Frame-Options (come da this W3 spec), ma frame-ancestors ha compatibilità limitata. Come da questi MDN Specs, non è supportato in IE o Edge.

Problemi correlati