Opzioni X-Frame Consenti-Da più domini


100

Ho un sito ASP.NET 4.0 IIS7.5 che devo proteggere usando l'intestazione X-Frame-Options.

Devo anche abilitare l'iframe delle pagine del mio sito dal mio stesso dominio e dalla mia app Facebook.

Attualmente ho il mio sito configurato con un sito intestato a:

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

Quando ho visualizzato la mia pagina Facebook con Chrome o Firefox, le pagine del mio sito (essendo iframe con la mia pagina Facebook) vengono visualizzate correttamente, ma in IE9, ottengo l'errore:

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

Come imposto il X-Frame-Options: ALLOW-FROMsupporto per più di un singolo dominio?

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


2
Questa sembra essere una limitazione nota: owasp.org/index.php/…
Pierre Ernst

Risposte:


109

X-Frame-Optionsè deprecato. Da MDN :

Questa funzionalità è stata rimossa dagli standard Web. Sebbene alcuni browser possano ancora supportarlo, è in procinto di essere abbandonato. Non utilizzarlo in progetti vecchi o nuovi. Le pagine o le app Web che lo utilizzano potrebbero interrompersi in qualsiasi momento.

L'alternativa moderna è l' Content-Security-Policyintestazione, che insieme a molte altre politiche può inserire nella lista bianca quali URL sono autorizzati a ospitare la tua pagina in un frame, utilizzando la frame-ancestorsdirettiva.
frame-ancestorssupporta più domini e persino caratteri jolly, ad esempio:

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

Purtroppo, per ora, Internet Explorer non supporta completamente i criteri di sicurezza dei contenuti .

AGGIORNAMENTO: MDN ha rimosso il commento sulla deprecazione. Ecco un commento simile dal Content Security Policy Level del W3C

La frame-ancestorsdirettiva rende obsoleto l' X-Frame-Optionsintestazione. Se una risorsa ha entrambe le politiche, la frame-ancestorspolitica DOVREBBE essere applicata e la X-Frame-Optionspolitica DOVREBBE essere ignorata.


14
frame-ancestors è contrassegnato come "API sperimentale e non deve essere utilizzato nel codice di produzione" su MDN. + X-Frame-Options non è deprecato ma "non standard" ma "è ampiamente supportato e può essere utilizzato insieme a CSP"
Jonathan Muller

1
@JonathanMuller - La formulazione è X-Frame-Optionscambiata e ora è meno severa. È un buon punto che è rischioso utilizzare una specifica che non è finalizzata. Grazie!
Kobi

2
Non riesco più a trovare l'avviso obsoleto su MDN. Mozilla ha cambiato opinione?
thomaskonrad

2
@ to0om - Grazie! Ho aggiornato la risposta con un altro commento. Potrei essere andato troppo forte nella mia risposta. Ad ogni modo, X-Frame-Optionsnon supporta più fonti.
Kobi

4
@ Kobi, penso che la risposta debba essere riorganizzata. La prima frase dice che questo è deprecato secondo l'MDN. Sarà meno fuorviante se aggiungi il tuo aggiornamento in alto (con un "AGGIORNAMENTO:" in grassetto). Grazie.
Kasun Gajasinghe

39

Da RFC 7034 :

I caratteri jolly o gli elenchi per dichiarare più domini in un'istruzione ALLOW-FROM non sono consentiti

Così,

Come imposto le opzioni X-Frame: ALLOW-FROM per supportare più di un singolo dominio?

Non puoi. Come soluzione alternativa, puoi utilizzare URL diversi per partner diversi. Per ogni URL puoi utilizzare il proprio X-Frame-Optionsvalore. Per esempio:

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

Perché yousite.compuoi semplicemente usare X-Frame-Options: deny.

BTW , per ora Chrome (e tutti i browser basati su webkit) non supporta affatto le ALLOW-FROM dichiarazioni.


1
Sembra che webkit ora supporti l' ALLOW-FROMutilizzo del collegamento fornito.
Jimi

3
@ Jimi No, non è così - l'ultimo commento sul collegamento in questione, dice che è necessario utilizzare invece un criterio CSP. Questa opzione continua a non funzionare in Chrome.
NickG

9

Negromante.
Le risposte fornite sono incomplete.

Innanzitutto, come già detto, non è possibile aggiungere più host di autorizzazione, che non sono supportati.
In secondo luogo, è necessario estrarre dinamicamente quel valore dal referrer HTTP, il che significa che non è possibile aggiungere il valore a Web.config, perché non è sempre lo stesso valore.

Sarà necessario eseguire il rilevamento del browser per evitare di aggiungere allow-from 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.

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

                    // /programming/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"
    ,"vmcompany1"
    ,"vmcompany2"
    ,"vmpostalservices"
    ,"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)
    {
        // /programming/441421/httpmodule-event-execution-order
        context.EndRequest += new System.EventHandler(context_EndRequest);
    }

    // context_EndRequest Code from above comes here


}

Successivamente è necessario aggiungere il modulo alla propria applicazione. Puoi farlo a livello di codice in Global.asax sovrascrivendo la funzione Init di HttpApplication, in questo modo:

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 puoi aggiungere voci a Web.config se non possiedi 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 in system.webServer è per IIS7 +, l'altra in system.web è per IIS 6.
Notare che è necessario impostare runAllManagedModulesForAllRequests su true, perché funziona correttamente.

La stringa nel tipo è nel formato "Namespace.Class, Assembly". Nota che se scrivi l'assembly in VB.NET invece che in C #, VB crea uno spazio dei nomi predefinito per ogni progetto, quindi la tua stringa sarà simile

"[DefaultNameSpace.Namespace].Class, Assembly"

Se vuoi evitare questo problema, scrivi la DLL in C #.


Penso che potresti voler rimuovere "vmswisslife" e "vmraiffeisen" dalla risposta in modo da non ottenere false correlazioni.
quetzalcoatl

@quetzalcoatl: li ho lasciati lì come esempio, non è una svista, non è in alcun modo riservato. Ma è vero, forse è meglio rimuoverli. Fatto.
Stefan Steiger il

7

Che ne dici di un approccio che non solo consente più domini, ma consente 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 . Quindi per IE, dobbiamo specificare ALLOW-FROM https: //.sharepoint.com

Affari complicati, ma possiamo portarli a termine conoscendo due fatti:

  1. Quando un iframe viene caricato, convalida solo le opzioni X-Frame alla prima richiesta. Una volta caricato l'iframe, puoi navigare all'interno dell'iframe e l'intestazione non viene controllata nelle richieste successive.

  2. Inoltre, quando viene caricato un iframe, il referer HTTP è l'URL iframe principale.

Puoi sfruttare questi due fatti 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 i domini basati sul dominio padre. In questo caso, ci assicuriamo che l'host termini in sharepoint.com mantenendo il nostro sito al sicuro dal clickjacking.

Mi piacerebbe ricevere feedback su questo approccio.


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

@StefanSteiger è vero, ma anche Chrome non presenta questo problema. Chrome e altri browser conformi agli standard seguono il modello CSP (Content Security Policy) più recente.
Peter P.

4

Secondo le specifiche MDN , X-Frame-Options: ALLOW-FROMnon è supportato in Chrome e il supporto è sconosciuto in Edge e Opera.

Content-Security-Policy: frame-ancestorssostituisce X-Frame-Options(secondo questa specifica W3 ), ma frame-ancestorsha una compatibilità limitata. In base a queste specifiche MDN , non è supportato in IE o Edge.


1

L'RFC per X-Frame-Options del campo di intestazione HTTP indica che il campo "ALLOW-FROM" nel valore dell'intestazione X-Frame-Options può contenere solo un dominio. Non sono consentiti più domini.

La RFC suggerisce una soluzione a questo problema. La soluzione è specificare il nome del dominio come parametro url nell'URL src dell'iframe. Il server che ospita l'URL iframe src può quindi controllare il nome di dominio fornito nei parametri dell'URL. Se il nome di dominio corrisponde a un elenco di nomi di dominio validi, il server può inviare l'intestazione X-Frame-Options con il valore: "ALLOW-FROM nome-dominio", dove nome di dominio è il nome del dominio che sta tentando di incorporare il contenuto remoto. Se il nome di dominio non viene fornito o non è valido, l'intestazione X-Frame-Options può essere inviata con il valore: "deny".


1

A rigor di termini no, non puoi.

È tuttavia possibile specificare X-Frame-Options: mysite.come quindi consentire subdomain1.mysite.come subdomain2.mysite.com. Ma sì, questo è ancora un dominio. Sembra esserci una soluzione alternativa per questo, ma penso che sia più facile leggerlo direttamente nelle specifiche RFC: https://tools.ietf.org/html/rfc7034

Vale anche la pena sottolineare che la frame-ancestordirettiva dell'intestazione Content-Security-Policy (CSP) rende obsoleto X-Frame-Options. Leggi di più qui .


0

Non esattamente la stessa cosa, ma potrebbe funzionare per alcuni casi: c'è un'altra opzione ALLOWALLche rimuoverà efficacemente la restrizione, il che potrebbe essere una cosa carina per gli ambienti di test / pre-produzione


Questo non è documentato su MDN.
andig

0

Ho dovuto aggiungere X-Frame-Options per IE e Content-Security-Policy per altri browser. Quindi ho fatto qualcosa come seguire.

if allowed_domains.present?
  request_host = URI.parse(request.referer)
  _domain = allowed_domains.split(" ").include?(request_host.host) ? "#{request_host.scheme}://#{request_host.host}" : app_host
  response.headers['Content-Security-Policy'] = "frame-ancestors #{_domain}"
  response.headers['X-Frame-Options'] = "ALLOW-FROM #{_domain}"
else
  response.headers.except! 'X-Frame-Options'
end

-4

Una possibile soluzione alternativa sarebbe utilizzare uno script "frame breaker" come descritto qui

Devi solo modificare l'istruzione "if" per verificare i tuoi domini consentiti.

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

Questa soluzione alternativa sarebbe sicura, credo. perché con javascript non abilitato non avrai alcun problema di sicurezza per 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.
Eric R.

-8

SÌ. Questo metodo consentiva più domini.

VB.NET

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

9
Questo sembra vanificare lo scopo di X-Frame-Options in quanto consente a qualsiasi sito di frame.
Andrey Shchekin

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

Se stai facendo questo, perché stai anche usando X-Frame-Options Header ... semplicemente ignoralo
vs4vijay
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.