X-Frame-Options Allow-From 여러 도메인


100

X-Frame-Options 헤더를 사용하여 보호해야하는 ASP.NET 4.0 IIS7.5 사이트가 있습니다.

또한 내 사이트 페이지가 내 페이스 북 앱뿐만 아니라 내 동일한 도메인에서 iframed되도록해야합니다.

현재 내 사이트는 다음 사이트로 구성되어 있습니다.

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

Chrome 또는 Firefox로 내 Facebook 페이지를 볼 때 내 사이트 페이지 (내 Facebook 페이지와 함께 표시됨)가 정상적으로 표시되지만 IE9에서는 오류가 발생합니다.

"이 페이지를 표시 할 수 없습니다…"( X-Frame_Options제한 사항 때문에 ).

X-Frame-Options: ALLOW-FROM두 개 이상의 도메인을 지원 하도록 을 설정하려면 어떻게합니까 ?

X-FRAME-OPTION 하나의 도메인 만 정의 할 수 있다면 새로운 기능이 근본적으로 결함이있는 것처럼 보입니다.


2
이 알려진 제한 될 것으로 보인다 : owasp.org/index.php/...
피에르 에른스트

답변:


109

X-Frame-Options더 이상 사용되지 않습니다. 에서 MDN :

이 기능은 웹 표준에서 제거되었습니다. 일부 브라우저는 여전히 지원할 수 있지만 삭제 중입니다. 이전 또는 새 프로젝트에서 사용하지 마십시오. 이를 사용하는 페이지 또는 웹 앱은 언제든지 중단 될 수 있습니다.

현대적인 대안은 Content-Security-Policy다른 많은 정책과 함께 frame-ancestors지시문을 사용하여 프레임에서 페이지를 호스팅 할 수있는 URL을 허용 목록에 추가 할 수 있는 헤더 입니다.
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정책을 무시해야합니다 (SHOULD).


14
프레임 조상은 MDN에서 "실험용 API이며 프로덕션 코드에서 사용해서는 안됩니다"로 표시됩니다. + X 프레임 - 옵션은 더 이상 사용되지되지 않고, "비표준"그러나 "광범위하게 지원하고 CSP와 함께 사용할 수 있습니다"
조나단 뮬러

1
@JonathanMuller-문구가 X-Frame-Options변경되었으며 현재 덜 심각합니다. 완성되지 않은 사양을 사용하는 것은 위험하다는 점이 좋은 점입니다. 감사!
Kobi 2015 년

2
MDN에서 더 이상 사용되지 않는 경고를 찾을 수 없습니다. Mozilla가 의견을 바 꾸었습니까?
thomaskonrad

2
@ to0om-감사합니다! 나는 다른 의견으로 답변을 업데이트했습니다. 내 대답이 너무 강해 졌을 수도 있습니다. 어느 쪽이든 X-Frame-Options여러 소스를 지원하지 않습니다.
Kobi

4
@Kobi, 대답은 재구성이 필요하다고 생각합니다. 첫 번째 문장은 이것이 MDN에 따라 더 이상 사용되지 않는다고 말합니다. 상단에 업데이트를 추가하면 오해의 소지가 줄어들 것입니다 (굵은 "UPDATE :"로 표시). 감사.
Kasun Gajasinghe

39

에서 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 문을 전혀 지원하지 않습니다 .


1
이제 웹킷 ALLOW-FROM이 제공하신 링크 사용을 지원 하는 것 같습니다 .
Jimi

3
@Jimi 아니요-문제의 링크에 대한 마지막 댓글은 대신 CSP 정책을 사용해야한다고 말합니다. 이 옵션은 여전히 ​​Chrome에서 작동하지 않습니다.
NickG

9

네크 로맨싱.
제공된 답변이 불완전합니다.

첫째, 이미 말했듯이 지원되지 않는 여러 허용 호스트를 추가 할 수 없습니다.
둘째, HTTP 리퍼러에서 해당 값을 동적으로 추출해야합니다. 즉, 항상 같은 값이 아니기 때문에 Web.config에 값을 추가 할 수 없습니다.

브라우저가 Chrome 일 때 allow-from을 추가하지 않도록 브라우저 감지를 수행해야합니다 (디버그 콘솔에 오류가 발생하여 콘솔이 빠르게 채워지거나 애플리케이션 속도가 느려질 수 있음). 또한 Edge를 Chrome으로 잘못 식별하므로 ASP.NET 브라우저 감지를 수정해야합니다.

이것은 모든 요청에서 실행되는 HTTP 모듈을 작성하여 ASP.NET에서 수행 할 수 있으며 요청의 리퍼러에 따라 모든 응답에 http-header를 추가합니다. Chrome의 경우 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 

HTTP 모듈 Init 함수에 context_EndRequest 함수를 등록해야합니다.

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


}

다음으로 애플리케이션에 모듈을 추가해야합니다. 다음과 같이 HttpApplication의 Init 함수를 재정 의하여 Global.asax에서 프로그래밍 방식으로이 작업을 수행 할 수 있습니다.

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로 설정해야합니다.

type의 문자열은 형식 "Namespace.Class, Assembly"입니다. C # 대신 VB.NET에서 어셈블리를 작성하는 경우 VB는 각 프로젝트에 대한 기본 네임 스페이스를 생성하므로 문자열은 다음과 같습니다.

"[DefaultNameSpace.Namespace].Class, Assembly"

이 문제를 피하려면 DLL을 C #으로 작성하십시오.


나는 당신이 대답에서 'vmswisslife'와 'vmraiffeisen'을 제거하여 잘못된 상관 관계를 얻지 않을 것이라고 생각합니다.
quetzalcoatl

@quetzalcoatl : 예를 들어 거기에 남겨 두었습니다. 그것은 감독이 아닙니다. 어떤 방식 으로든 기밀이 아닙니다. 그러나 사실, 아마도 그들을 제거하는 것이 좋습니다. 끝난.
Stefan Steiger

7

여러 도메인을 허용 할뿐만 아니라 동적 도메인을 허용하는 접근 방식은 어떻습니까?

여기서 사용 사례는 iframe을 통해 Sharepoint 내에서 사이트를로드하는 Sharepoint 앱 부분입니다. 문제는 sharepoint에 https://yoursite.sharepoint.com 과 같은 동적 하위 도메인이 있다는 것 입니다. 따라서 IE의 경우 ALLOW-FROM https : //.sharepoint.com을 지정해야합니다.

까다로운 사업이지만 두 가지 사실을 알면 완료 할 수 있습니다.

  1. iframe이로드되면 첫 번째 요청에서만 X-Frame-Options의 유효성을 검사합니다. iframe이로드되면 iframe 내에서 탐색 할 수 있으며 후속 요청에서 헤더를 확인하지 않습니다.

  2. 또한 iframe이로드되면 HTTP 리퍼러가 상위 iframe URL이됩니다.

이 두 가지 사실을 서버 측에서 활용할 수 있습니다. 루비에서는 다음 코드를 사용하고 있습니다.

  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에서 끝나는 것을 확인하여 사이트를 클릭 재킹으로부터 안전하게 보호합니다.

이 접근 방식에 대한 피드백을 듣고 싶습니다.


2
주의 : 호스트가 "fakesharepoint.com"이면 중단됩니다. 정규식은 다음과 같아야합니다./\.sharepoint\.com$/
nitsas 2015

@StefanSteiger 맞습니다.하지만 Chrome에서도이 문제가 발생하지 않습니다. Chrome 및 더 많은 표준 준수 브라우저는 최신 콘텐츠 보안 정책 (CSP) 모델을 따릅니다.
Peter P.

4

당으로 MDN 사양 , X-Frame-Options: ALLOW-FROM크롬에서 지원 및 지원 가장자리와 오페라에서 알 수 없습니다.

Content-Security-Policy: frame-ancestorsX-Frame-Options( 이 W3 사양에 따라 ) 무시 되지만 frame-ancestors호환성이 제한됩니다. 이러한 MDN 사양 에 따라 IE 또는 Edge에서는 지원되지 않습니다.


1

HTTP 헤더 필드 X-Frame-Options에 대한 RFC 에 따르면 X-Frame-Options 헤더 값의 "ALLOW-FROM"필드는 하나의 도메인 만 포함 할 수 있습니다. 여러 도메인은 허용되지 않습니다.

RFC는이 문제에 대한 해결 방법을 제안합니다. 해결책은 도메인 이름을 iframe src url에 url 매개 변수로 지정하는 것입니다. 그런 다음 iframe src URL을 호스팅하는 서버는 url 매개 변수에 지정된 도메인 이름을 확인할 수 있습니다. 도메인 이름이 유효한 도메인 이름 목록과 일치하면 서버는 "ALLOW-FROM domain-name"값과 함께 X-Frame-Options 헤더를 보낼 수 있습니다. 여기서 domain name은 시도하려는 도메인의 이름입니다. 원격 콘텐츠를 포함합니다. 도메인 이름이 지정되지 않았거나 유효하지 않은 경우 X-Frame-Options 헤더를 "deny"값으로 전송할 수 있습니다.


1

엄격히 말하면 안됩니다.

그러나 지정할 수 X-Frame-Options: mysite.com있으므로 subdomain1.mysite.comsubdomain2.mysite.com. 그러나 예, 그것은 여전히 ​​하나의 도메인입니다. 이에 대한 해결 방법이 있지만 RFC 사양 ( https://tools.ietf.org/html/rfc7034) 에서 직접 읽는 것이 가장 쉬운 방법이라고 생각합니다.

Content-Security-Policy (CSP) 헤더의 frame-ancestor지시문이 X-Frame-Options를 사용 하지 않는다는 점도 지적 할 가치가 있습니다. 여기에서 자세한 내용을 읽어보십시오 .


0

똑같지는 않지만 일부 경우에는 작동 할 수 있습니다 ALLOWALL. 제한을 효과적으로 제거 할 수있는 또 다른 옵션 이 있습니다. 이는 테스트 / 사전 프로덕션 환경에 적합 할 수 있습니다.


이것은 MDN에 문서화되어 있지 않습니다.
andig

0

IE 용 X-Frame-Options와 다른 브라우저 용 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

-4

한 가지 가능한 해결 방법은 여기에 설명 된 "프레임 브레이커"스크립트를 사용하는 것입니다.

허용 된 도메인을 확인하려면 "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;
   }

이 해결 방법은 안전하다고 생각합니다. 자바 스크립트를 사용하지 않으면 페이지를 구성하는 악성 웹 사이트에 대한 보안 문제가 없기 때문입니다.


1
이것은 top.location을 호출 할 때 동일한 오리진 정책으로 인해 작동하지 않습니다.
Eric R.

-8

예. 이 방법은 여러 도메인을 허용했습니다.

VB.NET

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

9
이것은 모든 사이트가 프레임을 만들 수 있기 때문에 X-Frame-Options의 목적을 무너 뜨리는 것 같습니다.
안드레이 Shchekin

5
이 답변은 해결책으로 좋은 기반이 될 수 있지만 request.urlreferer.tostring ()이 허용하려는 출처 중 하나 인 경우에만이 코드를 실행하도록 추가 논리가 필요합니다.
Zergleb

당신은 왜 당신도 X-프레임 - 옵션 헤더를 사용하고,이 일을하는 경우 ... 그냥 무시
vs4vijay
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.