X-Frame-Optionen Zulassen von mehreren Domänen

99

Ich habe eine ASP.NET 4.0 IIS7.5-Site, die ich mit dem X-Frame-Options-Header sichern muss.

Ich muss auch aktivieren, dass meine Seiten von meiner Domain sowie von meiner Facebook-App aus iframed werden.

Derzeit habe ich meine Site mit einer Site mit der Überschrift konfiguriert:

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

Wenn ich meine Facebook-Seite mit Chrome oder Firefox angesehen habe, werden die Seiten meiner Websites (die mit meiner Facebook-Seite versehen sind) in Ordnung angezeigt, aber unter IE9 wird folgende Fehlermeldung angezeigt:

"Diese Seite kann nicht angezeigt werden ..." (aufgrund der X-Frame_OptionsEinschränkung).

Wie stelle ich ein X-Frame-Options: ALLOW-FROM, dass mehr als eine einzelne Domain unterstützt wird?

X-FRAME-OPTION Eine neue Funktion zu sein, scheint grundlegend fehlerhaft zu sein, wenn nur eine einzige Domäne definiert werden kann.

user1340663
quelle
2
Dies scheint eine bekannte Einschränkung zu sein: owasp.org/index.php/…
Pierre Ernst

Antworten:

108

X-Frame-Optionsist veraltet. Von MDN :

Diese Funktion wurde aus den Webstandards entfernt. Obwohl einige Browser dies möglicherweise noch unterstützen, wird es gerade gelöscht. Verwenden Sie es nicht in alten oder neuen Projekten. Seiten oder Web-Apps, die es verwenden, können jederzeit beschädigt werden.

Die moderne Alternative ist der Content-Security-PolicyHeader, der zusammen mit vielen anderen Richtlinien mithilfe der frame-ancestorsDirektive auflisten kann, welche URLs Ihre Seite in einem Frame in einem Frame hosten dürfen .
frame-ancestorsunterstützt mehrere Domänen und sogar Platzhalter, zum Beispiel:

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

Leider unterstützt Internet Explorer die Content-Security-Policy derzeit nicht vollständig .

UPDATE: MDN hat den Verfallskommentar entfernt. Hier ist ein ähnlicher Kommentar von W3Cs Content Security Policy Level

Die frame-ancestorsDirektive veraltet den X-Frame-OptionsHeader. Wenn eine Ressource beide Richtlinien hat, frame-ancestorsMUSS die Richtlinie erzwungen und die X-Frame-OptionsRichtlinie ignoriert werden.

Kobi
quelle
14
Frame-Vorfahren sind auf MDN als "experimentelle API" gekennzeichnet und sollten nicht im Produktionscode verwendet werden. + X-Frame-Optionen sind nicht veraltet, aber "nicht standardisiert", sondern "werden weitgehend unterstützt und können in Verbindung mit CSP verwendet werden"
Jonathan Muller
1
@ JonathanMuller - Der Wortlaut hat sich X-Frame-Optionsgeändert und ist jetzt weniger streng. Es ist ein guter Punkt, dass es riskant ist, eine Spezifikation zu verwenden, die nicht finalisiert ist. Vielen Dank!
Kobi
2
Ich kann die korrigierte Warnung auf MDN nicht mehr finden. Hat Mozilla ihre Meinung geändert?
Thomaskonrad
2
@ to0om - Danke! Ich habe die Antwort mit einem weiteren Kommentar aktualisiert. Ich bin vielleicht zu stark in meiner Antwort angekommen. In beiden Fällen werden X-Frame-Optionsnicht mehrere Quellen unterstützt.
Kobi
4
@Kobi, ich denke, die Antwort muss neu organisiert werden. Der allererste Satz besagt, dass dies gemäß MDN veraltet ist. Es ist weniger irreführend, wenn Sie Ihr Update oben hinzufügen (mit einem fett gedruckten "UPDATE:"). Vielen Dank.
Kasun Gajasinghe
39

Aus RFC 7034 :

Platzhalter oder Listen zum Deklarieren mehrerer Domänen in einer ALLOW-FROM-Anweisung sind nicht zulässig

So,

Wie stelle ich die X-Frame-Optionen: ALLOW-FROM so ein, dass mehr als eine einzelne Domain unterstützt wird?

Das kannst du nicht. Um dieses Problem zu umgehen, können Sie verschiedene URLs für verschiedene Partner verwenden. Für jede URL können Sie einen eigenen X-Frame-OptionsWert verwenden. Beispielsweise:

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

Denn yousite.comdu kannst es einfach benutzen X-Frame-Options: deny.

BTW , denn jetzt Chrome (und alle Webkit-basierten Browsern) unterstützt keine ALLOW-FROM Aussagen überhaupt.

vbo
quelle
1
Es sieht so aus, als ob das Webkit jetzt die ALLOW-FROMVerwendung des von Ihnen angegebenen Links unterstützt .
Jimi
3
@ Jimi Nein, tut es nicht - der letzte Kommentar zu dem fraglichen Link besagt, dass Sie stattdessen eine CSP-Richtlinie verwenden müssen. Diese Option funktioniert in Chrome immer noch nicht.
NickG
9

Nekromantie.
Die angegebenen Antworten sind unvollständig.

Erstens können Sie, wie bereits gesagt, nicht mehrere zulässige Hosts hinzufügen, was nicht unterstützt wird.
Zweitens müssen Sie diesen Wert dynamisch aus dem HTTP-Referrer extrahieren. Dies bedeutet, dass Sie den Wert nicht zu Web.config hinzufügen können, da er nicht immer der gleiche Wert ist.

Es ist erforderlich, eine Browsererkennung durchzuführen, um zu vermeiden, dass bei Chrome ein Browser hinzugefügt wird (dies führt zu einem Fehler in der Debug-Konsole, der die Konsole schnell füllen oder die Anwendung verlangsamen kann). Dies bedeutet auch, dass Sie die ASP.NET-Browsererkennung ändern müssen, da Edge fälschlicherweise als Chrome identifiziert wird.

Dies kann in ASP.NET durch Schreiben eines HTTP-Moduls erfolgen, das bei jeder Anforderung ausgeführt wird und das abhängig vom Referrer der Anforderung für jede Antwort einen http-Header anfügt. Für Chrome muss eine Inhaltssicherheitsrichtlinie hinzugefügt werden.

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

Sie müssen die Funktion context_EndRequest in der Init-Funktion des HTTP-Moduls registrieren.

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


}

Als nächstes müssen Sie das Modul zu Ihrer Anwendung hinzufügen. Sie können dies entweder programmgesteuert in Global.asax tun, indem Sie die Init-Funktion der HttpApplication wie folgt überschreiben:

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

        }


    }


}

oder Sie können Einträge zu Web.config hinzufügen, wenn Sie den Quellcode der Anwendung nicht besitzen:

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

Der Eintrag in system.webServer ist für IIS7 +, der andere in system.web ist für IIS 6.
Beachten Sie, dass Sie runAllManagedModulesForAllRequests auf true setzen müssen, damit es ordnungsgemäß funktioniert.

Die Zeichenfolge vom Typ hat das Format "Namespace.Class, Assembly". Beachten Sie, dass VB, wenn Sie Ihre Assembly in VB.NET anstelle von C # schreiben, für jedes Projekt einen Standard-Namespace erstellt, sodass Ihre Zeichenfolge wie folgt aussieht

"[DefaultNameSpace.Namespace].Class, Assembly"

Wenn Sie dieses Problem vermeiden möchten, schreiben Sie die DLL in C #.

Stefan Steiger
quelle
Ich denke, Sie möchten vielleicht 'vmswisslife' und 'vmraiffeisen' aus der Antwort entfernen, damit es nicht zu falschen Korrelationen kommt.
Quetzalcoatl
@quetzalcoatl: Ich habe sie dort als Beispiel gelassen, es ist kein Versehen, es ist in keiner Weise vertraulich. Aber wahr, vielleicht besser entfernen. Getan.
Stefan Steiger
7

Wie wäre es mit einem Ansatz, der nicht nur mehrere Domänen zulässt, sondern auch dynamische Domänen.

Der Anwendungsfall hier ist ein Sharepoint-App-Teil, der unsere Site innerhalb von Sharepoint über einen Iframe lädt. Das Problem ist, dass Sharepoint über dynamische Subdomains wie https://yoursite.sharepoint.com verfügt . Für IE müssen wir also ALLOW-FROM https: //.sharepoint.com angeben

Kniffliges Geschäft, aber wir können es schaffen, wenn wir zwei Fakten kennen:

  1. Wenn ein Iframe geladen wird, werden die X-Frame-Optionen nur bei der ersten Anforderung überprüft. Sobald der Iframe geladen ist, können Sie innerhalb des Iframes navigieren und der Header wird bei nachfolgenden Anforderungen nicht überprüft.

  2. Wenn ein Iframe geladen wird, ist der HTTP-Referer die übergeordnete Iframe-URL.

Sie können diese beiden Fakten serverseitig nutzen. In Ruby verwende ich den folgenden Code:

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

Hier können wir Domänen basierend auf der übergeordneten Domäne dynamisch zulassen. In diesem Fall stellen wir sicher, dass der Host auf sharepoint.com endet und unsere Website vor Clickjacking geschützt ist.

Ich würde gerne Feedback zu diesem Ansatz hören.

Peter P.
quelle
2
Achtung: Dies bricht ab, wenn der Host "fakesharepoint.com" ist. Die Regex sollte sein:/\.sharepoint\.com$/
Nitsas
@StefanSteiger das stimmt, aber Chrome hat dieses Problem auch nicht. Chrome und weitere standardkonforme Browser folgen dem neueren CSP-Modell (Content Security Policy).
Peter P.
4

Gemäß den MDN Spezifikationen , X-Frame-Options: ALLOW-FROMin Chrome und Unterstützung ist nicht unbekannt in Rand- und Opera unterstützt.

Content-Security-Policy: frame-ancestorsüberschreibt X-Frame-Options(gemäß dieser W3-Spezifikation ), ist jedoch frame-ancestorsnur eingeschränkt kompatibel. Gemäß diesen MDN-Spezifikationen wird es in IE oder Edge nicht unterstützt.

Andrew
quelle
1

Der RFC für das HTTP-Headerfeld X-Frame-Options besagt, dass das Feld "ALLOW-FROM" im Headerwert X-Frame-Options nur eine Domäne enthalten darf. Mehrere Domains sind nicht erlaubt.

Der RFC schlägt eine Problemumgehung für dieses Problem vor. Die Lösung besteht darin, den Domänennamen als URL-Parameter in der iframe src-URL anzugeben. Der Server, der die iframe src-URL hostet, kann dann den in den URL-Parametern angegebenen Domänennamen überprüfen. Wenn der Domänenname mit einer Liste gültiger Domänennamen übereinstimmt, kann der Server den X-Frame-Options-Header mit dem Wert "ALLOW-FROM-Domänenname" senden, wobei der Domänenname der Name der Domäne ist, die dies versucht Betten Sie den Remote-Inhalt ein. Wenn der Domainname nicht angegeben oder ungültig ist, kann der X-Frame-Options-Header mit dem Wert "verweigern" gesendet werden.

Nadir Latif
quelle
1

Genau genommen nein, du kannst nicht.

Sie können jedoch angeben X-Frame-Options: mysite.comund daher zulassen subdomain1.mysite.comund subdomain2.mysite.com. Aber ja, das ist immer noch eine Domain. Es gibt zwar eine Problemumgehung, aber ich denke, es ist am einfachsten, dies direkt in den RFC-Spezifikationen zu lesen: https://tools.ietf.org/html/rfc7034

Es ist auch erwähnenswert, dass die frame-ancestorDirektive des Content-Security-Policy (CSP) -Headers die X-Frame-Optionen überholt. Lesen Sie hier mehr .

Jim Aho
quelle
0

Nicht genau das Gleiche, könnte aber in einigen Fällen funktionieren: Es gibt eine andere Option, mit ALLOWALLder die Einschränkung effektiv aufgehoben wird. Dies ist möglicherweise eine gute Sache für Test- / Vorproduktionsumgebungen

Willyfrog
quelle
Dies ist auf MDN nicht dokumentiert.
andig
0

Ich musste X-Frame-Optionen für IE und Content-Security-Policy für andere Browser hinzufügen. Also habe ich so etwas wie verfolgt.

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
jbmyid
quelle
-4

Eine mögliche Problemumgehung wäre die Verwendung eines "Frame-Breaker" -Skripts, wie hier beschrieben

Sie müssen nur die "if" -Anweisung ändern, um nach Ihren zulässigen Domains zu suchen.

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

Diese Problemumgehung wäre sicher, denke ich. Wenn Javascript nicht aktiviert ist, haben Sie keine Sicherheitsbedenken hinsichtlich einer schädlichen Website, die Ihre Seite umrahmt.

SinaX
quelle
1
Dies funktioniert aufgrund derselben Ursprungsrichtlinie beim Aufrufen von top.location nicht.
Eric R.
-8

JA. Diese Methode erlaubte mehrere Domänen.

VB.NET

response.headers.add("X-Frame-Options", "ALLOW-FROM " & request.urlreferer.tostring())
user4778040
quelle
9
Dies scheint den Zweck von X-Frame-Optionen zu zunichte zu machen, da jede Site Frames erstellen kann.
Andrey Shchekin
5
Diese Antwort scheint eine gute Basis als Lösung zu sein, benötigt jedoch zusätzliche Logik, damit dieser Code nur ausgeführt wird, wenn request.urlreferer.tostring () einer der Ursprünge ist, die Sie zulassen möchten.
Zergleb
Wenn Sie dies tun, warum verwenden Sie überhaupt X-Frame-Options-Header ... ignorieren Sie es einfach
vs4vijay