codememo

X-Frame-옵션 여러 도메인에서 허용

tipmemo 2023. 5. 18. 21:03
반응형

X-Frame-옵션 여러 도메인에서 허용

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

또한 페이스북 앱뿐만 아니라 동일한 도메인에서 사이트 페이지를 프레임화할 수 있어야 합니다.

현재 다음 사이트로 구성된 사이트가 있습니다.

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새로운 기능이 되는 것은 단일 도메인만 정의될 수 있다면 근본적으로 결함이 있는 것처럼 보입니다.

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

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

현대적인 대안은 헤더이며, 다른 많은 정책과 함께 지시문을 사용하여 프레임에서 페이지를 호스팅할 수 있는 URL을 화이트리스트에 추가할 수 있습니다.
frame-ancestors에서는 여러 도메인과 와일드카드까지 지원합니다. 예를 들어 다음과 같습니다.

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

안타깝게도 현재 Internet Explorer는 콘텐츠-보안-정책을 완전히 지원하지 않습니다.

업데이트: MDN이 사용되지 않는 주석을 제거했습니다.다음은 W3C의 콘텐츠 보안 정책 수준과 유사한 의견입니다.

frame-ancestors지시는 더 이상 쓸모가 없습니다.X-Frame-Options에 두 정책이 되어 있는 경우 리소스는 두 가지 정책을 모두 합니다.frame-ancestors정책을 시행해야 하며,X-Frame-Options정책은 무시해야 합니다.

RFC 7034에서:

하나의 ALLOW-FROM 문에 여러 도메인을 선언하는 와일드카드 또는 목록은 허용되지 않습니다.

그렇게,

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

그럴수는 없어요.해결 방법으로 파트너별로 다른 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, 현재 크롬(및 모든 웹킷 기반 브라우저)은 지원하지 않습니다. ALLOW-FROM일체의 진술

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

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

브라우저가 Chrom인 경우 allow-from을 추가하지 않도록 브라우저 탐지를 수행해야 합니다(디버깅 콘솔에서 오류가 발생하여 콘솔이 빠르게 채워지거나 응용 프로그램이 느려질 수 있습니다).또한 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"
    ,"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-module 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)
    {
        // https://stackoverflow.com/questions/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>

시의항다니목. 하나인 용입니다.webServer는 시스템의 다른 IIS7+용입니다.6을 것입니다.
run AllManagedModulesForAllRequests' true라는 파일이 있습니다.

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

"[DefaultNameSpace.Namespace].Class, Assembly"

이 문제를 방지하려면 DLL을 C#으로 작성합니다.

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

여기서 사용 사례는 iframe을 통해 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 에서 종료되어 클릭재킹으로부터 사이트를 안전하게 보호합니다.

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

MDN 사양에 따라,X-Frame-Options: ALLOW-FROM는 Chrome에서 지원되지 않으며 Edge 및 Opera에서 지원되지 않습니다.

Content-Security-Policy: frame-ancestors오버라이드X-Frame-Options(이 W3 사양에 따름), 그러나frame-ancestors호환성이 제한되어 있습니다.이 MDN 사양에 따르면 IE 또는 Edge에서는 지원되지 않습니다.

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

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

엄밀하게 말하면 안 됩니다.

그러나 지정할 수 있습니다.X-Frame-Options: mysite.com그러므로 허용합니다.subdomain1.mysite.com그리고.subdomain2.mysite.com하지만 네, 그것은 여전히 하나의 영역입니다.이에 대한 몇 가지 해결 방법이 있지만 RFC 사양에서 직접 읽는 것이 가장 쉬울 것 같습니다. https://www.rfc-editor.org/rfc/rfc7034

또한 CSP(Content-Security-Policy) 헤더의frame-ancestordirective는 X-Frame-Options를 폐지합니다.자세한 내용은 여기를 참조하십시오.

같지는 가 있을 수 : . 다른 옵션이 있습니다.ALLOWALL 프로덕션 할 수 있습니다. 이는 테스트/사전 프로덕션 환경에서 유용할 수 있습니다.

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

여러 도메인 및 하위 도메인에 대해 사용할 수 있는 규칙Apache그리고..htaccess아래와 같습니다.

Header always append Content-Security-Policy "frame-ancestors 'self' site1 site2;"

예:

은 아규래칙허니다합용다음만은▁only다니만 허용합니다.yoursite (self),https://example1.com/그리고.https://example2.com을 놓다iFrameyoursite.

Header always append Content-Security-Policy "frame-ancestors 'self' https://example1.com/ https://example.com;"

여기 참조 링크가 있습니다.

동일한 코드를 next.config.js에 넣습니다.

module.exports = {  
    async headers() {
        return [
            {
                source: '/((?!embed).*)',
                headers: [
                    {
                        key: 'X-Frame-Options',
                        value: 'SAMEORIGIN',
                    }
                ]
            }
        ];
    }
}

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

허용된 도메인을 확인하려면 "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가 활성화되지 않으면 악의적인 웹 사이트의 페이지 프레임 구성에 대한 보안 문제가 발생하지 않기 때문에 이 해결 방법은 안전할 것입니다.

예. 이 메서드는 다중 도메인을 허용합니다.

VB.NET

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

언급URL : https://stackoverflow.com/questions/10205192/x-frame-options-allow-from-multiple-domains

반응형