X-Frame-Options Permetti-Da più domini

Ho un sito IIS7.5 di asp.net 4.0 che ho bisogno di proteggere usando l’opzione delle intestazioni x-frame

Devo anche abilitare le mie pagine del sito ad essere iframed dal mio stesso dominio e dalla mia app di 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 visto la mia pagina Facebook con Chrome o FireFox le mie pagine dei siti (essendo iframed con la mia pagina facebook) sono visualizzate ok, ma sotto IE9, ho ricevuto l’errore

“questa pagina non può essere visualizzata …” (a causa della restrizione X-Frame_Options ).

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

X-FRAME-OPTION essendo una nuova funzionalità sembra fondamentalmente difettosa se solo un singolo dominio può essere definito.

X-Frame-Options è obsoleto. Da MDN :

Questa funzionalità è 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 è l’intestazione Content-Security-Policy , che insieme a molte altre politiche è in grado di elencare in bianco gli URL autorizzati ad ospitare la pagina in un frame, utilizzando 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 ; 

Sfortunatamente, per ora, Internet Explorer non supporta completamente la politica di Content-Security .

AGGIORNAMENTO: MDN ha rimosso il commento di ritiro. Ecco un commento simile dal Livello della politica di sicurezza dei contenuti del W3C

La direttiva frame-ancestors obsoleta l’intestazione X-Frame-Options . Se una risorsa ha entrambi i criteri, la politica dei frame-ancestors DEVE essere applicata e la politica delle X-Frame-Options DOVREBBE essere ignorata.

Da RFC 7034 :

Non sono consentiti i caratteri jolly o gli elenchi per dichiarare più domini in un’istruzione ALLOW-FROM

Così,

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

Non puoi Come soluzione alternativa puoi utilizzare URL diversi per partner diversi. Per ogni URL è ansible utilizzare il proprio valore X-Frame-Options . Per esempio:

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

Per yousite.com puoi semplicemente usare X-Frame-Options: deny .

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

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

Tricky business, ma possiamo farcela sapendo due fatti:

  1. Quando viene caricato un iframe, convalida solo le X-Frame-Options alla prima richiesta. Una volta caricato l’iframe, è ansible 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.

Puoi 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 dynamicmente 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 sentire feedback su questo approccio.

Necromancing.
Le risposte fornite sono incomplete.

Innanzitutto, come già detto, non è ansible aggiungere più host allow-from, che non è supportato.
In secondo luogo, è necessario estrarre dynamicmente quel valore dal referrer HTTP, il che significa che non è ansible aggiungere il valore a Web.config, perché non è sempre lo stesso valore.

Sarà necessario eseguire il rilevamento del browser per evitare l’aggiunta di 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.

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

È 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 alla propria applicazione. Puoi farlo a livello di codice in Global.asax sostituendo 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:

             

La voce in system.webServer è per IIS7 +, l'altra in system.web è per IIS 6.
Si noti che è necessario impostare runAllManagedModulesForAllRequests su true, perché funzioni correttamente.

La stringa in type è nel formato "Namespace.Class, Assembly" . Si noti che se si scrive l'assembly in VB.NET anziché in C #, VB crea uno spazio dei nomi predefinito per ogni progetto, quindi la stringa assomiglierà

 "[DefaultNameSpace.Namespace].Class, Assembly" 

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

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

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

Content-Security-Policy: frame-ancestors sovrascrivono X-Frame-Options (come da questa specifica W3 ), ma i frame-ancestors hanno una compatibilità limitata. Come da queste specifiche MDN , non è supportato in IE o Edge.

Una soluzione ansible sarebbe utilizzare uno script “interruzione di frame” come descritto qui

Devi solo modificare l’istruzione “if” per verificare i 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 sarebbe sicura, penso. perché con javascript non abilitato non avrai preoccupazioni per la sicurezza di un sito Web dannoso che inquadra la tua pagina.

SÌ. Questo metodo consentiva più domini.

VB.NET

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