X-Frame-Options Allow-From plusieurs domaines

J’ai un site asp.net 4.0 IIS7.5 qui doit être sécurisé à l’aide de l’option d’en-tête x-frame

Je dois également permettre que les pages de mon site soient iframées à partir de mon même domaine et de mon application facebook.

Actuellement, mon site est configuré avec un site intitulé:

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

Lorsque j’ai consulté ma page Facebook avec Chrome ou FireFox, les pages de mes sites (en cours d’affichage avec ma page facebook) sont affichées correctement, mais sous IE9, je reçois une erreur.

“Cette page ne peut pas être affichée…” (à cause de la ressortingction X-Frame_Options ).

Comment définir les X-Frame-Options: ALLOW-FROM pour prendre en charge plusieurs domaines?

X-FRAME-OPTION étant une nouvelle fonctionnalité, elle semble fondamentalement erronée si un seul domaine peut être défini.

X-Frame-Options est obsolète. De MDN :

Cette fonctionnalité a été supprimée des normes Web. Bien que certains navigateurs puissent toujours le supporter, il est en train d’être supprimé. Ne l’utilisez pas dans des projets anciens ou nouveaux. Les pages ou applications Web qui l’utilisent peuvent se rompre à tout moment.

L’alternative moderne est l’en Content-Security-Policy tête Content-Security-Policy , qui, avec de nombreuses autres stratégies, peut répertorier les URL autorisées à héberger votre page dans un cadre, en utilisant la directive frame-ancestors .
frame-ancestors prend en charge plusieurs domaines et même des caractères génériques, par exemple:

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

Malheureusement, pour le moment, Internet Explorer ne prend pas totalement en charge la stratégie de sécurité du contenu .

MISE À JOUR: MDN a supprimé son commentaire de dépréciation. Voici un commentaire similaire du niveau de la politique de sécurité du contenu du W3C

La directive frame-ancestors rend obsolète l’en X-Frame-Options tête X-Frame-Options . Si une ressource possède les deux stratégies, la stratégie frame-ancestors DEVRAIT être appliquée et la stratégie X-Frame-Options DEVRAIT être ignorée.

De RFC 7034 :

Les caractères génériques ou les listes permettant de déclarer plusieurs domaines dans une instruction ALLOW-FROM ne sont pas autorisés.

Alors,

Comment définir les options X-Frame: ALLOW-FROM pour prendre en charge plusieurs domaines?

Vous ne pouvez pas Pour contourner ce problème, vous pouvez utiliser différentes URL pour différents partenaires. Pour chaque URL, vous pouvez utiliser sa propre valeur X-Frame-Options . Par exemple:

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

Pour yousite.com vous pouvez simplement utiliser X-Frame-Options: deny .

BTW , pour l’instant Chrome (et tous les navigateurs Webkit) ne supporte pas du tout les instructions ALLOW-FROM .

Que diriez-vous d’une approche qui non seulement autorise plusieurs domaines, mais autorise des domaines dynamics?

Le cas d’utilisation est une partie de l’application Sharepoint qui charge notre site à l’intérieur de Sharepoint via un iframe. Le problème est que SharePoint a des sous-domaines dynamics tels que https://votresite.sharepoint.com . Donc pour IE, nous devons spécifier ALLOW-FROM https: //.sharepoint.com

Des affaires épineuses, mais nous pouvons le faire en sachant deux choses:

  1. Lorsqu’une iframe est chargée, elle valide uniquement les options X-Frame sur la première requête. Une fois l’iframe chargé, vous pouvez naviguer dans l’iframe et l’en-tête n’est pas coché sur les requêtes suivantes.

  2. De même, lorsqu’un iframe est chargé, le référent HTTP est l’URL du parent parent.

Vous pouvez tirer parti de ces deux aspects côté serveur. En ruby, j’utilise le code suivant:

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

Ici, nous pouvons autoriser dynamicment les domaines basés sur le domaine parent. Dans ce cas, nous nous assurons que l’hôte termine par sharepoint.com en gardant notre site à l’abri des clics de clic.

J’aimerais entendre vos commentaires sur cette approche.

Nécromancie.
Les réponses fournies sont incomplètes.

Tout d’abord, comme nous l’avons déjà dit, vous ne pouvez pas append plusieurs hôtes autorisés, ce qui n’est pas pris en charge.
Deuxièmement, vous devez extraire dynamicment cette valeur du référent HTTP, ce qui signifie que vous ne pouvez pas append la valeur à Web.config, car ce n’est pas toujours la même valeur.

Il sera nécessaire de faire une détection par navigateur pour éviter d’append des permissions lorsque le navigateur est Chrome (cela produit une erreur sur la console de débogage, qui peut rapidement remplir la console ou ralentir l’application). Cela signifie également que vous devez modifier la détection du navigateur ASP.NET, car elle identifie à tort Edge comme Chrome.

Cela peut être fait dans ASP.NET en écrivant un module HTTP qui s’exécute sur chaque requête, qui ajoute un en-tête http pour chaque réponse, en fonction du référent de la requête. Pour Chrome, il doit append une stratégie de sécurité du contenu.

 // https://stackoverflow.com/questions/31870789/check-whether-browser-is-chrome-or-edge public class BrowserInfo { public System.Web.HttpBrowserCapabilities Browser { get; set; } public ssortingng Name { get; set; } public ssortingng Version { get; set; } public ssortingng Platform { get; set; } public bool IsMobileDevice { get; set; } public ssortingng MobileBrand { get; set; } public ssortingng 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.ToSsortingng(); } 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 ssortingng host = System.Web.HttpContext.Current.Request.UrlReferrer.Scheme + System.Uri.SchemeDelimiter + System.Web.HttpContext.Current.Request.UrlReferrer.Authority ; ssortingng selfAuth = System.Web.HttpContext.Current.Request.Url.Authority; ssortingng refAuth = System.Web.HttpContext.Current.Request.UrlReferrer.Authority; // SQL.Log(System.Web.HttpContext.Current.Request.RawUrl, System.Web.HttpContext.Current.Request.UrlReferrer.OriginalSsortingng, 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.SsortingngComparer.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); ssortingng contentSecurityPolicy = ssortingng.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 ssortingng[] s_allowedHosts = new ssortingng[] { "localhost:49533" ,"localhost:52257" ,"vmswisslife" ,"vmraiffeisen" ,"vmpost" ,"example.com" }; public static bool IsHostAllowed(ssortingng host) { return Contains(s_allowedHosts, host); } // End Function IsHostAllowed public static bool Contains(ssortingng[] allowed, ssortingng 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 

Vous devez enregistrer la fonction context_EndRequest dans la fonction HTTP-module Init.

 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 } 

Ensuite, vous devez append le module à votre application. Vous pouvez soit le faire par programme dans Global.asax en remplaçant la fonction Init de HttpApplication, comme ceci:

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

ou vous pouvez append des entrées à Web.config si vous ne possédez pas le code source de l'application:

             

L'entrée dans system.webServer est pour IIS7 +, l'autre dans system.web est pour IIS 6.
Notez que vous devez définir runAllManagedModulesForAllRequests sur true pour que cela fonctionne correctement.

La chaîne de caractères est au format "Namespace.Class, Assembly" . Notez que si vous écrivez votre assembly dans VB.NET au lieu de C #, VB crée un espace de noms par défaut pour chaque projet afin que votre chaîne ressemble à

 "[DefaultNameSpace.Namespace].Class, Assembly" 

Si vous souhaitez éviter ce problème, écrivez la DLL en C #.

Pas exactement la même chose, mais pourrait fonctionner dans certains cas: il existe une autre option ALLOWALL qui supprime efficacement la ressortingction, ce qui pourrait être une bonne chose pour les environnements de test / pré-production

Selon les spécifications MDN , X-Frame-Options: ALLOW-FROM n’est pas pris en charge dans Chrome et la prise en charge est inconnue dans Edge et Opera.

Content-Security-Policy: frame-ancestors remplace les X-Frame-Options (conformément à cette spécification W3 ), mais la compatibilité des frame-ancestors est limitée. Conformément à ces spécifications MDN , elles ne sont pas sockets en charge dans IE ou Edge.

Une solution possible serait d’utiliser un script “frame-breaker” comme décrit ici

Il vous suffit de modifier l’instruction “if” pour vérifier vos domaines autorisés.

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

Cette solution de contournement serait sûre, je pense. parce que javascript n’est pas activé, vous n’aurez aucun problème de sécurité concernant un site Web malveillant encadrant votre page.

OUI. Cette méthode permettait plusieurs domaines.

VB.NET

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