X-Frame-Options Allow-From multiple domain

100

Saya memiliki situs ASP.NET 4.0 IIS7.5 yang saya perlukan diamankan menggunakan header X-Frame-Options.

Saya juga perlu mengaktifkan halaman situs saya untuk di-ifram dari domain saya yang sama serta dari aplikasi facebook saya.

Saat ini saya memiliki situs saya yang dikonfigurasi dengan situs menuju:

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

Ketika saya melihat halaman Facebook saya dengan Chrome atau Firefox, halaman situs saya (dibingkai dengan halaman facebook saya) ditampilkan dengan baik, tetapi di bawah IE9, saya mendapatkan kesalahan:

"halaman ini tidak dapat ditampilkan…" (karena X-Frame_Optionspembatasan).

Bagaimana cara mengatur X-Frame-Options: ALLOW-FROMagar mendukung lebih dari satu domain?

X-FRAME-OPTION menjadi fitur baru tampaknya memiliki kelemahan mendasar jika hanya satu domain yang dapat ditentukan.

pengguna1340663
sumber
2
Sepertinya ini adalah batasan yang diketahui: owasp.org/index.php/…
Pierre Ernst

Jawaban:

109

X-Frame-Optionssudah ditinggalkan. Dari MDN :

Fitur ini telah dihapus dari standar Web. Meskipun beberapa browser mungkin masih mendukungnya, ini sedang dalam proses untuk dihentikan. Jangan gunakan di proyek lama atau baru. Halaman atau aplikasi Web yang menggunakannya dapat rusak kapan saja.

Alternatif modern adalah Content-Security-Policytajuk, yang bersama banyak kebijakan lain dapat membuat daftar putih URL apa yang diizinkan untuk menghosting laman Anda dalam sebuah bingkai, menggunakan frame-ancestorsarahan.
frame-ancestorsmendukung banyak domain dan bahkan karakter pengganti, misalnya:

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

Sayangnya, untuk saat ini, Internet Explorer tidak mendukung sepenuhnya Kebijakan Keamanan Konten .

UPDATE: MDN telah menghapus komentar penghentian mereka. Berikut komentar serupa dari Level Kebijakan Keamanan Konten W3C

The frame-ancestorsdirektif obsoletes yang X-Frame-Optionssundulan. Jika sumber daya memiliki kedua kebijakan, frame-ancestorskebijakan HARUS diterapkan dan X-Frame-Optionskebijakan HARUS diabaikan.

Kobi
sumber
14
frame-nenek moyang ditandai sebagai "API eksperimental dan tidak boleh digunakan dalam kode produksi" di MDN. + X-Frame-Options tidak digunakan lagi tetapi "non-standar" tetapi "didukung secara luas dan dapat digunakan bersama dengan CSP"
Jonathan Muller
1
@JonathanMuller - Kata-kata di X-Frame-Optionsberubah, dan sekarang tidak terlalu parah. Ini adalah poin yang bagus bahwa berisiko menggunakan spesifikasi yang belum diselesaikan. Terima kasih!
Kobi
2
Saya tidak dapat lagi menemukan peringatan yang salah di MDN. Apakah Mozilla mengubah pendapat mereka?
thomaskonrad
2
@ to0om - Terima kasih! Saya memperbarui jawaban dengan komentar lain. Aku mungkin terlalu kuat menjawabnya. Bagaimanapun, X-Frame-Optionstidak mendukung banyak sumber.
Kobi
4
@Kobi, saya pikir jawabannya perlu diatur ulang. Kalimat pertama mengatakan bahwa ini tidak berlaku lagi sesuai MDN. Ini tidak akan menyesatkan jika Anda menambahkan pembaruan Anda di bagian atas (dengan "UPDATE:" berwarna tebal). Terima kasih.
Kasun Gajasinghe
39

Dari RFC 7034 :

Karakter pengganti atau daftar untuk mendeklarasikan beberapa domain dalam satu pernyataan ALLOW-FROM tidak diizinkan

Begitu,

Bagaimana cara mengatur X-Frame-Options: ALLOW-FROM untuk mendukung lebih dari satu domain?

Tidak boleh. Sebagai solusinya, Anda dapat menggunakan URL yang berbeda untuk mitra yang berbeda. Untuk setiap URL Anda dapat menggunakan X-Frame-Optionsnilainya sendiri . Sebagai contoh:

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

Untuk yousite.comAnda bisa menggunakan X-Frame-Options: deny.

BTW , untuk saat ini Chrome (dan semua browser berbasis webkit) tidak mendukung ALLOW-FROM pernyataan sama sekali.

vbo
sumber
1
Sepertinya webkit sekarang mendukung ALLOW-FROMpenggunaan tautan yang Anda berikan.
Jimi
3
@Jimi Tidak, tidak - komentar terakhir di tautan yang dipermasalahkan mengatakan bahwa Anda perlu menggunakan kebijakan CSP. Opsi ini masih tidak berfungsi di Chrome.
NickG
9

Necromancing.
Jawaban yang diberikan tidak lengkap.

Pertama, seperti yang telah dikatakan, Anda tidak dapat menambahkan beberapa host yang diizinkan, yang tidak didukung.
Kedua, Anda perlu mengekstrak nilai tersebut secara dinamis dari pengarah HTTP, yang berarti Anda tidak dapat menambahkan nilai ke Web.config, karena nilainya tidak selalu sama.

Deteksi browser perlu dilakukan untuk menghindari penambahan allow-from saat browser adalah Chrome (ini menghasilkan kesalahan pada konsol debug, yang dapat dengan cepat mengisi konsol, atau membuat aplikasi menjadi lambat). Itu juga berarti Anda perlu memodifikasi deteksi browser ASP.NET, karena salah mengidentifikasi Edge sebagai Chrome.

Ini dapat dilakukan di ASP.NET dengan menulis modul HTTP yang dijalankan pada setiap permintaan, yang menambahkan http-header untuk setiap respons, tergantung pada pengarah permintaan. Untuk Chrome, perlu menambahkan Content-Security-Policy.

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

Anda perlu mendaftarkan fungsi context_EndRequest dalam fungsi 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)
    {
        // /programming/441421/httpmodule-event-execution-order
        context.EndRequest += new System.EventHandler(context_EndRequest);
    }

    // context_EndRequest Code from above comes here


}

Selanjutnya Anda perlu menambahkan modul ke aplikasi Anda. Anda dapat melakukan ini secara terprogram di Global.asax dengan mengganti fungsi Init dari HttpApplication, seperti ini:

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

        }


    }


}

atau Anda dapat menambahkan entri ke Web.config jika Anda tidak memiliki kode sumber aplikasi:

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

Entri di system.webServer adalah untuk IIS7 +, yang lainnya di system.Web adalah untuk IIS 6.
Perhatikan bahwa Anda perlu menyetel runAllManagedModulesForAllRequests ke true, untuk itu berfungsi dengan baik.

Jenis string dalam format "Namespace.Class, Assembly". Perhatikan bahwa jika Anda menulis rakitan Anda di VB.NET daripada C #, VB membuat Namespace default untuk setiap proyek, sehingga string Anda akan terlihat seperti

"[DefaultNameSpace.Namespace].Class, Assembly"

Jika Anda ingin menghindari masalah ini, tulis DLL di C #.

Stefan Steiger
sumber
Saya pikir Anda mungkin ingin menghapus 'vmswisslife' dan 'vmraiffeisen' dari jawaban sehingga tidak akan mendapatkan korelasi yang salah.
quetzalcoatl
@quetzalcoatl: Saya meninggalkannya di sana sebagai contoh, ini bukan pengawasan, ini sama sekali tidak rahasia. Tapi benar, mungkin lebih baik menghapusnya. Selesai.
Stefan Steiger
7

Bagaimana dengan pendekatan yang tidak hanya memungkinkan beberapa domain, tetapi memungkinkan domain dinamis.

Kasus penggunaan di sini adalah dengan bagian aplikasi Sharepoint yang memuat situs kami di dalam Sharepoint melalui iframe. Masalahnya adalah sharepoint memiliki subdomain dinamis seperti https://yoursite.sharepoint.com . Jadi untuk IE, kita perlu menentukan ALLOW-FROM https: //.sharepoint.com

Bisnis yang rumit, tetapi kita dapat menyelesaikannya dengan mengetahui dua fakta:

  1. Saat iframe dimuat, itu hanya memvalidasi X-Frame-Options pada permintaan pertama. Setelah iframe dimuat, Anda dapat menavigasi di dalam iframe dan header tidak diperiksa pada permintaan berikutnya.

  2. Selain itu, saat iframe dimuat, pengarah HTTP adalah url iframe induk.

Anda dapat memanfaatkan dua sisi server fakta ini. Di ruby, saya menggunakan kode berikut:

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

Di sini kami dapat secara dinamis mengizinkan domain berdasarkan domain induk. Dalam kasus ini, kami memastikan bahwa host diakhiri dengan sharepoint.com yang menjaga situs kami aman dari pembajakan klik.

Saya ingin mendengar umpan balik tentang pendekatan ini.

Peter P.
sumber
2
Perhatian: ini rusak jika tuan rumah adalah "fakesharepoint.com". Regex harus:/\.sharepoint\.com$/
nitsas
@StefanSteiger benar, tetapi Chrome juga tidak mengalami masalah ini. Chrome dan browser lain yang memenuhi standar standar mengikuti model Kebijakan Keamanan Konten (CSP) yang lebih baru.
Peter P.
4

Sesuai dengan Spesifikasi MDN , X-Frame-Options: ALLOW-FROMtidak didukung di Chrome dan dukungan tidak diketahui di Edge dan Opera.

Content-Security-Policy: frame-ancestorsmenimpa X-Frame-Options(sesuai spesifikasi W3 ini ), tetapi frame-ancestorsmemiliki kompatibilitas terbatas. Sesuai dengan Spesifikasi MDN ini , itu tidak didukung di IE atau Edge.

Andrew
sumber
1

RFC untuk HTTP Header Field X-Frame-Options menyatakan bahwa kolom "ALLOW-FROM" di nilai header X-Frame-Options hanya dapat berisi satu domain. Beberapa domain tidak diperbolehkan.

RFC menyarankan solusi untuk masalah ini. Solusinya adalah dengan menentukan nama domain sebagai parameter url di url src iframe. Server yang menghosting url src iframe kemudian dapat memeriksa nama domain yang diberikan di parameter url. Jika nama domain cocok dengan daftar nama domain yang valid, maka server dapat mengirim header X-Frame-Options dengan nilai: "ALLOW-FROM domain-name", di mana nama domain adalah nama domain yang mencoba sematkan konten jarak jauh. Jika nama domain tidak diberikan atau tidak valid, maka header X-Frame-Options dapat dikirimkan dengan nilai: "deny".

Nadir Latif
sumber
1

Sebenarnya tidak, Anda tidak bisa.

Namun Anda dapat menentukan X-Frame-Options: mysite.comdan oleh karena itu mengizinkan subdomain1.mysite.comdan subdomain2.mysite.com. Tapi ya, itu masih satu domain. Kebetulan ada beberapa solusi untuk ini, tetapi saya pikir paling mudah untuk membacanya langsung di spesifikasi RFC: https://tools.ietf.org/html/rfc7034

Perlu juga ditunjukkan bahwa frame-ancestorpetunjuk header Content-Security-Policy (CSP) mengabaikan X-Frame-Options. Baca lebih lanjut di sini .

Jim Aho
sumber
0

Tidak persis sama, tetapi dapat berfungsi untuk beberapa kasus: ada opsi lain ALLOWALLyang secara efektif akan menghapus batasan, yang mungkin merupakan hal yang baik untuk lingkungan pengujian / pra-produksi

Willyfrog
sumber
Ini tidak didokumentasikan di MDN.
andig
0

Saya harus menambahkan X-Frame-Options untuk IE dan Content-Security-Policy untuk browser lain. Jadi saya melakukan sesuatu seperti mengikuti.

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.dll
sumber
-4

Salah satu solusi yang mungkin adalah menggunakan skrip "pemecah bingkai" seperti yang dijelaskan di sini

Anda hanya perlu mengubah pernyataan "jika" untuk memeriksa domain Anda yang diizinkan.

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

Solusi ini akan aman, saya pikir. karena dengan javascript tidak diaktifkan Anda tidak akan memiliki masalah keamanan tentang situs web berbahaya yang membingkai halaman Anda.

SinaX
sumber
1
Ini tidak akan berfungsi karena kebijakan asal yang sama saat memanggil top.location.
Eric R.
-8

IYA. Metode ini memungkinkan banyak domain.

VB.NET

response.headers.add("X-Frame-Options", "ALLOW-FROM " & request.urlreferer.tostring())
pengguna4778040
sumber
9
Ini tampaknya mengalahkan tujuan X-Frame-Options karena memungkinkan situs apa pun untuk membingkai.
Andrey Shchekin
5
Jawaban ini sepertinya bisa menjadi dasar yang baik sebagai solusi tetapi membutuhkan logika tambahan sehingga hanya mengeksekusi kode ini jika request.urlreferer.tostring () adalah salah satu sumber yang ingin Anda izinkan.
Zergleb
Jika Anda melakukan ini, mengapa Anda bahkan menggunakan X-Frame-Options Header ... abaikan saja
vs4vijay