X-Frame-Options Permettent-à Partir de multiples domaines
j'ai un asp.net 4,0 IIS7.5 site que j'ai besoin de sécuriser en utilisant les en-têtes X-frame option
je dois aussi permettre à mes pages de site d'être iframed à partir de mon même domaine ainsi que de mon application facebook.
actuellement, j'ai mon site configuré avec un site intitulé de:
Response.Headers.Add("X-Frame-Options", "ALLOW-FROM SAMEDOMAIN, www.facebook.com/MyFBSite")
quand j'ai vu ma page Facebook avec Chrome ou FireFox mes pages de sites (étant iframed avec ma page facebook) sont affichés ok, mais sous IE9, Je reçois le message d'erreur
"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 plus d'un domaine?
X-FRAME-OPTION
être une nouvelle caractéristique semble fondamentalement défectueux Si seulement un seul domaine peut être défini.
9 réponses
X-Frame-Options
est déprécié. De MDN :
cette fonctionnalité a été supprimée des normes Web. Bien que certains navigateurs puissent encore le supporter, 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 se casser à tout moment.
l'alternative moderne est le Content-Security-Policy
" en-tête, qui avec beaucoup d'autres politiques peut énumérer en blanc ce que les URLs sont autorisés à 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 l'instant, Internet Explorer ne supporte pas entièrement Content-Security-Policy .
UPDATE: MDN a supprimé leur commentaire de dépréciation. Voici un commentaire similaire de niveau de la Politique de sécurité du contenu du W3C
Le
frame-ancestors
directive obsoletes leX-Frame-Options
en-tête. Si une ressource a les deux politiques, la Politiqueframe-ancestors
devrait être appliquée et la PolitiqueX-Frame-Options
devrait être ignorée.
From RFC 7034 :
les caractères génériques ou les listes pour déclarer plusieurs domaines dans une seule déclaration ALLOW-FROM ne sont pas autorisés
,
comment définir X-Frame-Options: ALLOW-FROM pour prendre en charge plus d'un domaine?
vous ne pouvez pas. Comme solution de contournement, vous pouvez utiliser des URLs différentes 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 utiliser X-Frame-Options: deny
.
BTW , pour le moment Chrome (et tous les navigateurs webkit) ne supporte pas ALLOW-FROM
déclarations à tous.
Que Diriez-vous d'une approche qui non seulement permet des domaines multiples, mais permet des domaines dynamiques.
le cas d'utilisation ici est avec une partie SharePoint app qui charge notre site à L'intérieur de Sharepoint via une iframe. Le problème est que sharepoint a des sous-domaines dynamiques tels que https://yoursite.sharepoint.com . Donc pour IE, nous avons besoin de spécifier ALLOW-FROM https://.sharepoint.com
affaire délicate, mais nous pouvons y arriver en sachant deux faits:
-
Lorsqu'une iframe est chargée, elle ne valide les Options X-Frame que sur la première demande. Une fois l'iframe chargée, vous pouvez naviguer dans l'iframe et l'en-tête n'est pas coché sur les requêtes suivantes.
-
aussi, lorsqu'une iframe est chargée, le référent HTTP est l'url parent iframe.
vous pouvez utiliser ces deux données côté serveur. Dans 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 sharepoint.com garder notre site à l'abri de clickjacking.
j'aimerais entendre des commentaires sur cette approche.
Nécromancie.
Les réponses sont incomplètes.
tout d'abord, comme déjà dit, vous ne pouvez pas ajouter plusieurs allow-from hosts, Cela n'est pas supporté.
Deuxièmement, vous devez extraire dynamiquement cette valeur à partir du referrer HTTP, ce qui signifie que vous ne pouvez pas ajouter la valeur à Web.config, car il n'est pas toujours la même valeur.
il sera nécessaire de faire la détection de navigateur pour éviter d'ajouter autoriser-à partir du moment où le navigateur est Chrome (il produit une erreur sur le débogueur - console, qui peut rapidement remplir la console, ou de rendre l'application lente). Cela signifie également que vous devez modifier la ASP.NET détection de navigateur, car il identifie à tort Edge comme Chrome.
cela peut être fait en 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 referrer de la requête. Pour Chrome, il faut ajouter Contenu De La Politique De Sécurité.
// /q/check-whether-browser-is-chrome-or-edge-24805/"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
// /q/x-frame-options-allow-from-multiple-domains-16660/"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)
{
// /q/httpmodule-event-execution-order-6853/"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 le système.webServer est pour IIS7+, l'autre dans le système.le web est pour IIS 6.
Notez que vous devez définir runAllManagedModulesForAllRequests à true, pour que cela fonctionne correctement.
la chaîne de caractères est au format "Namespace.Class, Assembly"
.
Notez que si vous écrivez votre assemblée VB.NET au lieu de C#, VB crée un Namespace par défaut pour chaque projet, donc votre chaîne ressemblera à
"[DefaultNameSpace.Namespace].Class, Assembly"
si vous voulez éviter ce problème, écrivez la DLL en C#.
pas exactement le même, mais pourrait fonctionner dans certains cas: il ya une autre option ALLOWALL
qui va effectivement supprimer la restriction, 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 supporté dans Chrome et le support est inconnu dans Edge et Opera.
Content-Security-Policy: frame-ancestors
remplace X-Frame-Options
(comme pour cette W3 spec ), mais frame-ancestors
a limité la compatibilité. Selon ces Spécifications MDN , il n'est pas pris en charge dans IE ou Edge.
j'ai dû ajouter X-Frame-Options pour IE et Content-Security-politique pour les autres navigateurs. Donc j'ai fait quelque chose comme suivre.
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
une solution possible serait d'utiliser un script "frame-breaker" comme décrit ici
vous avez juste besoin de modifier la déclaration" si " 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 serait sûre, je pense. parce que javascript n'est pas activé, vous n'aurez aucun problème de sécurité avec un site Web malveillant cadrant votre page.
Oui. Cette méthode permettait plusieurs domaines.
VB.NET
response.headers.add("X-Frame-Options", "ALLOW-FROM " & request.urlreferer.tostring())