Подтвердить что ты не робот

Параметры X-Frame-Allow - из нескольких доменов

У меня есть сайт ASP.NET 4.0 IIS7.5, который необходимо защитить с помощью заголовка X-Frame-Options.

Мне также нужно разрешить вставку страниц моего сайта с того же домена, а также из приложения Facebook.

В настоящее время у меня настроен сайт с заголовком:

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

Когда я просматривал свою страницу в Facebook с помощью Chrome или Firefox, мои страницы сайтов (будучи вставленными в мою страницу Facebook) отображаются нормально, но в IE9 я получаю сообщение об ошибке:

"this page cannot be displayed…" (because of the X-Frame_Options restriction).

Как настроить X-Frame-Options: ALLOW-FROM на поддержку нескольких доменов?

X-FRAME-OPTION, будучи новой функцией, кажется фундаментально ошибочным, если можно определить только один домен.

4b9b3361

Ответ 1

X-Frame-Options устарел. Из MDN:

Эта функция удалена из веб-стандартов. Хотя некоторые браузеры все еще могут его поддерживать, он находится в процессе отбрасывания. Не используйте его в старых или новых проектах. Страницы или веб-приложения, использующие его, могут прерываться в любое время.

Современной альтернативой является заголовок Content-Security-Policy, который по многим другим политикам может белым списком, каким URL-адресам разрешено размещать вашу страницу в кадр, используя директиву frame-ancestors.
frame-ancestors поддерживает несколько доменов и даже подстановочные знаки, например:

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

К сожалению, на данный момент Internet Explorer не полностью поддерживает Content-Security-Policy.

ОБНОВЛЕНИЕ: MDN удалил свой комментарий об отказе. Здесь аналогичный комментарий от Уровень безопасности контента W3C

Директива frame-ancestors обходит заголовок X-Frame-Options. Если ресурс имеет обе политики, политика frame-ancestors ДОЛЖНА быть введена в действие, а политика X-Frame-Options ДОЛЖНА проигнорироваться.

Ответ 2

От RFC 7034:

Подстановочные знаки или списки для объявления нескольких доменов в одном заявлении ALLOW-FROM не разрешены

Итак,

Как установить X-Frame-Options: ALLOW-FROM для поддержки более чем одного домена?

Вы не можете. В качестве обходного пути вы можете использовать разные URL-адреса для разных партнеров. Для каждого URL-адреса вы можете использовать его собственное значение X-Frame-Options. Например:

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

Для yousite.com вы можете просто использовать X-Frame-Options: deny.

BTW, на данный момент Chrome (и все веб-браузеры) не поддерживает ALLOW-FROM заявления на все.

Ответ 3

Как насчет подхода, который позволяет не только использовать несколько доменов, но и динамические домены.

В данном случае используется часть приложения Sharepoint, которая загружает наш сайт внутри Sharepoint через iframe. Проблема в том, что sharepoint имеет динамические поддомены, такие как https://yoursite.sharepoint.com. Поэтому для IE нам нужно указать ALLOW-FROM https://.sharepoint.com

Сложное дело, но мы можем сделать это, зная два факта:

  1. Когда загружается iframe, он проверяет X-Frame-Options только по первому запросу. После загрузки iframe вы можете перемещаться внутри iframe, и заголовок не проверяется при последующих запросах.

  2. Кроме того, когда загружен iframe, HTTP-ссылка является родительским URL-адресом iframe.

Вы можете использовать эти два факта на стороне сервера. В ruby я использую следующий код:

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

Здесь мы можем динамически разрешать домены на основе родительского домена. В этом случае мы гарантируем, что хост заканчивается на sharepoint.com, чтобы защитить наш сайт от перехвата кликов.

Я хотел бы услышать отзывы об этом подходе.

Ответ 4

Necromancing.

Предоставленные ответы являются неполными.

Во-первых, как уже говорилось, вы не можете добавить несколько разрешенных хостов, которые не поддерживаются.

Во-вторых, вам нужно динамически извлекать это значение из HTTP-реферера, а это значит, что вы не можете добавить значение в Web.config, потому что это не всегда одно и то же значение.

Необходимо будет выполнить обнаружение браузера, чтобы избежать добавления разрешений, когда браузер Chrome (он вызывает ошибку на консоли отладки, которая может быстро заполнить консоль или сделать приложение медленным). Это также означает, что вам необходимо изменить обнаружение браузера ASP.NET, поскольку оно неправильно идентифицирует Edge как Chrome.

Это можно сделать в ASP.NET, написав HTTP-модуль, который выполняется для каждого запроса, который добавляет HTTP-заголовок для каждого ответа, в зависимости от реферера запроса. Для Chrome необходимо добавить 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 

Вам необходимо зарегистрировать функцию context_EndRequest в функции Init-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


}

Затем вам нужно добавить модуль в свое приложение. Вы можете сделать это программно в Global.asax, переопределив функцию Init HttpApplication, например:

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

        }


    }


}

или вы можете добавлять записи в Web.config, если у вас нет исходного кода приложения:

      <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 для IIS7 +, другая в system.web для IIS 6.
Обратите внимание, что вам нужно установить для runAllManagedModulesForAllRequests значение true, поскольку оно работает правильно.

Строка типа находится в формате "Namespace.Class, Assembly". Обратите внимание: если вы пишете свою сборку в VB.NET вместо С#, VB создает пространство имен по умолчанию для каждого проекта, поэтому ваша строка будет выглядеть как

"[DefaultNameSpace.Namespace].Class, Assembly"

Если вы хотите избежать этой проблемы, напишите DLL в С#.

Ответ 5

В соответствии с Спецификации MDN, X-Frame-Options: ALLOW-FROM не поддерживается в Chrome и поддержка неизвестна в Edge и Opera.

Content-Security-Policy: frame-ancestors переопределяет X-Frame-Options (согласно эта спецификация W3), но frame-ancestors имеет ограниченную совместимость. В соответствии с этими спецификациями MDN он не поддерживается в IE или Edge.

Ответ 6

В RFC для поля HTTP-заголовка X-Frame-Options указано, что поле "ALLOW-FROM" в значении заголовка X-Frame-Options может содержать только один домен. Несколько доменов не допускаются.

RFC предлагает обойти эту проблему. Решением является указание имени домена в качестве параметра url в URL-адресе iframe. Затем сервер, на котором размещается URL-адрес iframe src, может проверить имя домена, указанное в параметрах URL-адреса. Если доменное имя соответствует списку допустимых доменных имен, то сервер может отправить заголовок X-Frame-Options со значением: "ALLOW-FROM domain-name", где domain name - это имя домена, который пытается встроить удаленный контент. Если доменное имя не указано или недопустимо, тогда заголовок X-Frame-Options может быть отправлен со значением "deny".

Ответ 7

Не совсем то же самое, но может работать в некоторых случаях: есть еще один вариант ALLOWALL, который эффективно удалит ограничение, что может быть приятным для тестирования/предварительных производственных сред

Ответ 8

Мне пришлось добавить X-Frame-Options для IE и Content-Security-Policy для других браузеров. Поэтому я сделал что-то вроде следующего.

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

Ответ 9

Строго говоря, нет, вы не можете.

Однако вы можете указать X-Frame-Options: mysite.com и, следовательно, разрешить subdomain1.mysite.com и subdomain2.mysite.com. Но да, это все еще один домен. Для этого есть некоторый обходной путь, но я думаю, что проще всего это прочитать непосредственно в спецификации RFC: https://tools.ietf.org/html/rfc7034

Также стоит отметить, что директива заголовка frame-ancestor Content-Security-Policy (CSP) устарела X-Frame-Options. Подробнее читайте здесь.

Ответ 10

Одним из возможных способов решения проблемы будет использование "фреймователя" script, как описано здесь

Вам просто нужно изменить оператор "if", чтобы проверить допустимые домены.

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

Обходной путь был бы безопасным, я думаю. потому что, если javascript не включен, у вас не будет проблем с безопасностью для вредоносного веб-сайта, создающего вашу страницу.

Ответ 11

ДА. Этот метод допускал несколько доменов.

VB.NET

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