X-Frame-Options Allow-Birden çok etki alanından


100

X-Frame-Options başlığını kullanarak güvenli hale getirilmesi gereken bir ASP.NET 4.0 IIS7.5 sitem var.

Ayrıca site sayfalarımın aynı alanımdan ve facebook uygulamamdan iç çerçeveye alınmasını sağlamam gerekiyor.

Şu anda sitem şu başlığı taşıyan bir site ile yapılandırılmış durumda:

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

Facebook sayfamı Chrome veya Firefox ile görüntülediğimde site sayfalarım (facebook sayfamla çerçeveleniyor) iyi görüntüleniyor, ancak IE9 altında şu hatayı alıyorum:

"bu sayfa görüntülenemiyor…" ( X-Frame_Optionskısıtlama nedeniyle ).

X-Frame-Options: ALLOW-FROMTek bir alan adından fazlasını destekleyecek şekilde nasıl ayarlarım ?

X-FRAME-OPTION yeni bir özellik olmak, yalnızca tek bir alan tanımlanabiliyorsa temelde kusurlu görünüyor.


2
Bu bilinen bir sınırlama gibi görünüyor: owasp.org/index.php/…
Pierre Ernst

Yanıtlar:


111

X-Frame-Optionskullanımdan kaldırıldı. Gönderen MDN'yi :

Bu özellik, Web standartlarından çıkarılmıştır. Bazı tarayıcılar hala desteklese de, bırakılma sürecindedir. Eski veya yeni projelerde kullanmayınız. Bunu kullanan sayfalar veya Web uygulamaları her an bozulabilir.

Modern alternatif, Content-Security-Policydiğer birçok politikanın yanı sıra, frame-ancestorsyönergeyi kullanarak sayfanızı bir çerçeve içinde barındırmasına izin verilen URL'leri beyaz listeye alabilen başlıktır .
frame-ancestorsbirden çok alanı ve hatta joker karakterleri destekler, örneğin:

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

Ne yazık ki şimdilik, Internet Explorer İçerik Güvenliği Politikasını tam olarak desteklemiyor .

GÜNCELLEME: MDN, kullanımdan kaldırma açıklamasını kaldırdı. İşte W3C'nin İçerik Güvenlik Politikası Düzeyinden benzer bir yorum

frame-ancestorsYönerge obsoletesX-Frame-Options başlığı. Bir kaynağın her iki politikası varsa, frame-ancestorspolitika uygulanmalı ve X-Frame-Optionspolitika yok sayılmalıdır.


14
çerçeve-üst öğeleri, MDN'de "deneysel API olarak işaretlenir ve üretim kodunda kullanılmamalıdır". + X-çerçeve-Seçenek kaldırıldı değildir, ancak "standart" değil "geniş bir şekilde desteklenmektedir ve CSP ile bağlantılı olarak kullanılabilen"
Jonathan Muller

1
@JonathanMuller - İfadeler X-Frame-Optionsdeğişti ve şimdi daha az şiddetli. Kesinleşmemiş bir spesifikasyon kullanmanın riskli olması iyi bir nokta. Teşekkürler!
Kobi

2
Artık MDN'de yanlış uyarıyı bulamıyorum. Mozilla fikrini değiştirdi mi?
thomaskonrad

2
@ to0om - Teşekkürler! Cevabı başka bir yorumla güncelledim. Cevabımda çok güçlenmiş olabilirim. Her iki durumda da X-Frame-Optionsbirden fazla kaynağı desteklemez.
Kobi

4
@Kobi, cevabın yeniden düzenlenmesi gerektiğini düşünüyorum. İlk cümle, bunun MDN'ye göre kullanımdan kaldırıldığını söylüyor. Güncellemenizi en üste eklerseniz daha az yanıltıcı olacaktır (kalın renkli "GÜNCELLEME:" ile). Teşekkürler.
Kasun Gajasinghe

39

Gönderen RFC 7034 :

Bir ALLOW-FROM deyiminde birden çok etki alanı bildiren joker karakterlere veya listelere izin verilmez

Yani,

X-Frame-Options: ALLOW-FROM'u birden fazla alanı destekleyecek şekilde nasıl ayarlayabilirim?

Yapamazsın. Çözüm olarak, farklı ortaklar için farklı URL'ler kullanabilirsiniz. Her URL için kendi X-Frame-Optionsdeğerini kullanabilirsiniz. Örneğin:

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

İçin yousite.comsadece kullanabilirsiniz X-Frame-Options: deny.

BTW , şimdilik Chrome (ve tüm webkit tabanlı tarayıcılar) ifadeleri hiç desteklemiyor ALLOW-FROM .


1
Görünüşe göre webkit artık ALLOW-FROMsağladığınız bağlantıyı kullanmayı destekliyor .
Jimi

3
@Jimi Hayır değil - söz konusu bağlantıdaki son yorum, bunun yerine bir CSP politikası kullanmanız gerektiğini söylüyor. Bu seçenek hala Chrome'da çalışmıyor.
NickG

9

Büyüleyici.
Verilen cevaplar eksik.

İlk olarak, daha önce de belirtildiği gibi, birden fazla izin verilen ana bilgisayar ekleyemezsiniz, bu desteklenmez.
İkinci olarak, bu değeri dinamik olarak HTTP yönlendiricisinden çıkarmanız gerekir; bu, değeri Web.config dosyasına ekleyemeyeceğiniz anlamına gelir, çünkü her zaman aynı değer değildir.

Tarayıcı Chrome olduğunda izin vermeyi önlemek için tarayıcı algılama yapmak gerekli olacaktır (hata ayıklama konsolunda, konsolu hızla doldurabilen veya uygulamayı yavaşlatabilen bir hata oluşturur). Bu aynı zamanda Edge'i yanlışlıkla Chrome olarak tanımladığı için ASP.NET tarayıcı algılamasını değiştirmeniz gerektiği anlamına gelir.

Bu, ASP.NET'te, her istekte çalışan, isteğin yönlendiricisine bağlı olarak her yanıt için bir http başlığı ekleyen bir HTTP modülü yazarak yapılabilir. Chrome için İçerik-Güvenlik-Politikası eklemesi gerekiyor.

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

Context_EndRequest işlevini HTTP modülü Init işlevinde kaydetmeniz gerekir.

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


}

Daha sonra modülü uygulamanıza eklemeniz gerekir. Bunu Global.asax içinde HttpApplication'ın Init işlevini geçersiz kılarak programlı olarak yapabilirsiniz, örneğin:

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

        }


    }


}

veya uygulama kaynak koduna sahip değilseniz Web.config dosyasına giriş ekleyebilirsiniz:

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

System.webServer'daki girdi IIS7 + içindir, diğeri system.web'deki IIS 6
içindir. RunAllManagedModulesForAllRequests'i true olarak ayarlamanız gerektiğini unutmayın, çünkü düzgün çalışır.

Türdeki dize biçimdedir "Namespace.Class, Assembly". Derlemenizi C # yerine VB.NET'te yazarsanız, VB her proje için varsayılan bir Ad Alanı oluşturur, böylece dizeniz şöyle görünür

"[DefaultNameSpace.Namespace].Class, Assembly"

Bu sorunu önlemek istiyorsanız, DLL'yi C # ile yazın.


Sanırım yanıttan "vmswisslife" ve "vmraiffeisen" i çıkarmak isteyebilirsiniz, böylece yanlış korelasyonlar olmaz.
quetzalcoatl

@quetzalcoatl: Onları örnek olarak orada bıraktım, bu bir gözetim değil, hiçbir şekilde gizli değil. Ama doğru, belki onları kaldırsan daha iyi olur. Bitti.
Stefan Steiger

7

Yalnızca birden fazla alana değil, aynı zamanda dinamik alanlara da izin veren bir yaklaşıma ne dersiniz?

Buradaki kullanım durumu, sitemizi bir iframe aracılığıyla Sharepoint'in içine yükleyen bir Sharepoint uygulaması bölümüdür. Sorun, sharepoint'in https://yoursite.sharepoint.com gibi dinamik alt alanlara sahip olmasıdır . Dolayısıyla, IE için, ALLOW-FROM https: //.sharepoint.com'u belirtmemiz gerekir.

Zor iş, ancak iki gerçeği bilerek halledebiliriz:

  1. Bir iframe yüklendiğinde, yalnızca ilk istekte X-Frame-Options'ı doğrular. İframe yüklendikten sonra, iframe içinde gezinebilirsiniz ve başlık sonraki isteklerde kontrol edilmez.

  2. Ayrıca, bir iframe yüklendiğinde, HTTP yönlendireni üst iframe url'sidir.

Sunucu tarafında bu iki olgudan yararlanabilirsiniz. Ruby'de şu kodu kullanıyorum:

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

Burada, ana etki alanına bağlı olarak etki alanlarına dinamik olarak izin verebiliriz. Bu durumda, sitemizi tıklama hırsızlığından koruyarak ana bilgisayarın sharepoint.com'da bitmesini sağlıyoruz.

Bu yaklaşımla ilgili geri bildirim almak isterim.


2
Dikkat: ana bilgisayar "fakesharepoint.com" ise bu kesilir. /\.sharepoint\.com$/
Normal

@StefanSteiger doğru, ancak Chrome da bu sorunu yaşamıyor. Chrome ve daha fazla standartla uyumlu tarayıcı, daha yeni İçerik Güvenliği Politikası (CSP) modelini izler.
Peter P.


1

X-Frame-Options HTTP Üstbilgi Alanı için RFC , X-Frame-Options başlık değerindeki "ALLOW-FROM" alanının yalnızca bir etki alanı içerebileceğini belirtir. Birden fazla alana izin verilmez.

RFC, bu sorun için bir çözüm önerir. Çözüm, etki alanı adını iframe src url'sinde bir url parametresi olarak belirtmektir. İframe src url'sini barındıran sunucu daha sonra url parametrelerinde verilen alan adını kontrol edebilir. Etki alanı adı, geçerli etki alanı adları listesiyle eşleşiyorsa, sunucu X-Frame-Options başlığını şu değerle gönderebilir: "ALLOW-FROM etki alanı adı", burada etki alanı adı denen etki alanının adıdır uzak içeriği katıştırın. Etki alanı adı verilmemişse veya geçerli değilse, X-Frame-Options başlığı şu değerle gönderilebilir: "deny".


1

Kesinlikle hayır, yapamazsınız.

Ancak belirtebilir X-Frame-Options: mysite.comve dolayısıyla izin verebilirsiniz subdomain1.mysite.comve subdomain2.mysite.com. Ama evet, bu hala bir alan adı. Bunun için bir geçici çözüm var, ancak bunu doğrudan RFC özelliklerinde okumak en kolay yoldur: https://tools.ietf.org/html/rfc7034

Ayrıca, Content-Security-Policy (CSP) üstbilgisinin frame-ancestorX-Frame-Options'ı geçersiz kıldığını belirtmekte fayda var . Daha fazlasını buradan okuyun .


0

Tam olarak aynı değil, ancak bazı durumlarda işe yarayabilir: ALLOWALLkısıtlamayı etkili bir şekilde kaldıracak başka bir seçenek daha var ki bu test / üretim öncesi ortamlar için güzel bir şey olabilir.


Bu MDN'de belgelenmemiştir.
andig

0

IE için X-Frame-Options ve diğer tarayıcılar için Content-Security-Policy eklemek zorunda kaldım. Ben de aşağıdaki gibi bir şey yaptım.

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

-4

Olası bir çözüm, burada açıklandığı gibi bir "çerçeve ayırıcı" komut dosyası kullanmak olabilir

İzin verilen alan adlarınızı kontrol etmek için "if" ifadesini değiştirmeniz yeterlidir.

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

Bu geçici çözüm güvenli olacaktır, sanırım. javascript etkinleştirilmediğinden, sayfanızı çerçeveleyen kötü amaçlı bir web sitesi hakkında hiçbir güvenlik endişeniz olmayacaktır.


1
Bu, top.location çağrılırken aynı başlangıç ​​ilkesi nedeniyle çalışmaz.
Eric R.

-8

EVET. Bu yöntem birden fazla alana izin verdi.

VB.NET

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

9
Bu, herhangi bir sitenin çerçeve oluşturmasına izin verdiği için X-Frame-Options'ın amacını bozuyor gibi görünüyor.
Andrey Shchekin

5
Bu yanıt, bir çözüm olarak iyi bir temel olabilir gibi görünüyor, ancak fazladan mantığa ihtiyaç duyuyor, bu nedenle bu kodu yalnızca request.urlreferer.tostring () izin vermek istediğiniz kaynaklardan biriyse yürütür.
Zergleb

Bunu yapıyorsanız, neden X-Frame-Options Başlığını kullanıyorsunuz ... görmezden
gelin
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.