J'ai un site asp.net 4.0 IIS7.5 dont j'ai besoin d'être sécurisé à l'aide de l'option d'en-têtes x-frame
Je dois également permettre aux pages de mon site d'être numérisé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 mon site (étant iframées avec ma page facebook) s'affichent correctement, mais sous IE9, le message d'erreur s'affiche.
"cette page ne peut pas être affichée…" (en raison de la restriction
X-Frame_Options
).
Comment définir le X-Frame-Options: ALLOW-FROM
pour prendre en charge plusieurs domaines?
X-FRAME-OPTION
étant une nouvelle fonctionnalité, cela semble fondamentalement imparfait si un seul domaine peut être défini.
X-Frame-Options
est obsolète. De MDN :
Cette fonctionnalité a été supprimée des standards Web. Bien que certains navigateurs puissent toujours le prendre en charge, il est en train d’être abandonné. Ne l'utilisez pas dans des projets anciens ou nouveaux. Les pages ou les applications Web qui l'utilisent peuvent être interrompues à tout moment. </ S>
L’alternative moderne est l’en-tête Content-Security-Policy
, qui, avec de nombreuses autres stratégies, permet de répertorier les URL autorisées à héberger votre page dans un cadre, à l’aide de 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 l'instant, Internet Explorer ne prend pas totalement en charge Content-Security-Policy .
METTRE À JOUR: MDN a supprimé son commentaire de désapprobation. Voici un commentaire similaire de Niveau de stratégie de sécurité du contenu du W3C
La directive
frame-ancestors
obsolète l'en-têteX-Frame-Options
. Si une ressource a les deux stratégies, la stratégieframe-ancestors
DEVRAIT être appliquée et la stratégieX-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 X-Frame-Options: ALLOW-FROM pour prendre en charge plusieurs domaines?
Tu ne peux pas. Pour résoudre 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 le moment Chrome (et tous les navigateurs Webkit) ne prend pas en charge les instructionsALLOW-FROM
du tout.
Qu'en est-il d'une approche qui non seulement autorise plusieurs domaines, mais autorise également les domaines dynamiques.
Le cas d'utilisation est ici avec une partie d'application Sharepoint qui charge notre site à l'intérieur de Sharepoint via un iframe. Le problème est que le point de partage possède des sous-domaines dynamiques tels que https://votresite.sharepoint.com . Donc, pour IE, nous devons spécifier ALLOW-FROM https: //.sharepoint.com
Une affaire délicate, mais nous pouvons le faire en sachant deux faits:
Lorsqu'un iframe est chargé, il valide uniquement les options X-Frame à la première demande. Une fois l'iframe chargé, vous pouvez naviguer dans l'iframe et l'en-tête n'est pas coché lors des demandes suivantes.
De plus, lorsqu'un iframe est chargé, le référent HTTP est l'URL parent iframe.
Vous pouvez tirer parti de ces deux faits 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 dynamiquement les domaines basés sur le domaine parent. Dans ce cas, nous nous assurons que l'hôte se termine par sharepoint.com en protégeant notre site des détournements de clic.
J'aimerais entendre vos commentaires sur cette approche.
Nécromancie.
Les réponses fournies sont incomplètes.
Premièrement, comme déjà dit, vous ne pouvez pas ajouter plusieurs hôtes autorisés, cela n'est pas supporté.
Deuxièmement, vous devez extraire dynamiquement cette valeur du référent HTTP, ce qui signifie que vous ne pouvez pas ajouter la valeur à Web.config, car ce n'est pas toujours la même valeur.
Il sera nécessaire de détecter le navigateur pour éviter d’ajouter à allow-from lorsque le navigateur est sous Chrome (une erreur sur la console de débogage peut se produire, ce 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 celle-ci identifie à tort Edge en tant que Chrome.
Cela peut être fait dans ASP.NET en écrivant un module HTTP qui s'exécute sur chaque demande et qui ajoute un en-tête http pour chaque réponse, en fonction du référent de la demande. Pour Chrome, il faut ajouter 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
Vous devez enregistrer la fonction context_EndRequest dans la fonction Init du module 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
}
Ensuite, vous devez ajouter le module à votre application. Vous pouvez 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 ajouter des entrées à Web.config si vous ne possédez pas le code source de l'application:
<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>
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 dans le type 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. Votre chaîne ressemblera donc à
"[DefaultNameSpace.Namespace].Class, Assembly"
Si vous voulez éviter ce problème, écrivez le DLL en C #.
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 X-Frame-Options
(comme indiqué par cette spécification W3 ), mais frame-ancestors
a une compatibilité limitée. Selon ces Specs MDN , il n'est pas pris en charge dans IE ou Edge.
Je devais ajouter X-Frame-Options pour IE et Content-Security-Policy pour les autres navigateurs . J'ai donc fait quelque chose comme suit.
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
Pas exactement la même chose, mais cela pourrait fonctionner dans certains cas: il existe une autre option ALLOWALL
qui supprimera effectivement la restriction, ce qui pourrait être une bonne chose pour les environnements de test/pré-production