X-Frame-Options Allow-From 여러 도메인
X-Frame-Options 헤더를 사용하여 보호해야하는 ASP.NET 4.0 IIS7.5 사이트가 있습니다.
또한 내 사이트 페이지가 내 Facebook 앱뿐만 아니라 동일한 도메인에서 iframe되도록해야합니다.
현재 내 사이트는 다음 사이트로 구성되어 있습니다.
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 :
이 기능은 웹 표준에서 제거되었습니다. 일부 브라우저는 여전히 지원할 수 있지만 삭제 중입니다. 이전 또는 새 프로젝트에서 사용하지 마십시오. 이를 사용하는 페이지 또는 웹 앱은 언제든지 중단 될 수 있습니다.
현대적인 대안은 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).
에서 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
문을 전혀 지원하지 않습니다 .
여러 도메인을 허용 할뿐만 아니라 동적 도메인을 허용하는 접근 방식은 어떻습니까?
여기서 사용 사례는 iframe을 통해 Sharepoint 내에서 사이트를로드하는 Sharepoint 앱 부분입니다. 문제는 sharepoint에 https://yoursite.sharepoint.com 과 같은 동적 하위 도메인이 있다는 것 입니다. 따라서 IE의 경우 ALLOW-FROM https : //.sharepoint.com을 지정해야합니다.
까다로운 사업이지만 두 가지 사실을 알면 완료 할 수 있습니다.
iframe이로드되면 첫 번째 요청에서만 X-Frame-Options의 유효성을 검사합니다. iframe이로드되면 iframe 내에서 탐색 할 수 있으며 후속 요청에서 헤더를 확인하지 않습니다.
또한 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에서 끝나는 것을 확인하여 사이트를 클릭 재킹으로부터 안전하게 보호합니다.
이 접근 방식에 대한 피드백을 듣고 싶습니다.
네크 로맨싱.
제공된 답변이 불완전합니다.
첫째, 이미 말했듯이 지원되지 않는 여러 허용 호스트를 추가 할 수 없습니다.
둘째, HTTP 리퍼러에서 해당 값을 동적으로 추출해야합니다. 즉, 항상 같은 값이 아니기 때문에 Web.config에 값을 추가 할 수 없습니다.
브라우저가 Chrome 일 때 allow-from을 추가하지 않도록 브라우저 감지를 수행해야합니다 (디버그 콘솔에 오류가 발생하여 콘솔이 빠르게 채워지거나 애플리케이션 속도가 느려질 수 있음). 또한 Edge를 Chrome으로 잘못 식별하므로 ASP.NET 브라우저 감지를 수정해야합니다.
이것은 모든 요청에서 실행되는 HTTP 모듈을 작성하여 ASP.NET에서 수행 할 수 있으며 요청의 리퍼러에 따라 모든 응답에 http-header를 추가합니다. 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
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)
{
// 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)
{
}
}
}
or you can add entries to Web.config if you don't own the application source-code:
<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>
The entry in system.webServer is for IIS7+, the other in system.web is for IIS 6.
Note that you need to set runAllManagedModulesForAllRequests to true, for that it works properly.
The string in type is in the format "Namespace.Class, Assembly"
. Note that if you write your assembly in VB.NET instead of C#, VB creates a default-Namespace for each project, so your string will look like
"[DefaultNameSpace.Namespace].Class, Assembly"
If you want to avoid this problem, write the DLL in C#.
As per the MDN Specifications, X-Frame-Options: ALLOW-FROM
is not supported in Chrome and support is unknown in Edge and Opera.
Content-Security-Policy: frame-ancestors
overrides X-Frame-Options
(as per this W3 spec), but frame-ancestors
has limited compatibility. As per these MDN Specs, it's not supported in IE or Edge.
The RFC for the HTTP Header Field X-Frame-Options states that the "ALLOW-FROM" field in the X-Frame-Options header value can only contain one domain. Multiple domains are not allowed.
The RFC suggests a work around for this problem. The solution is to specify the domain name as a url parameter in the iframe src url. The server that hosts the iframe src url can then check the domain name given in the url parameters. If the domain name matches a list of valid domain names, then the server can send the X-Frame-Options header with the value: "ALLOW-FROM domain-name", where domain name is the name of the domain that is trying to embed the remote content. If the domain name is not given or is not valid, then the X-Frame-Options header can be sent with the value: "deny".
Not exactly the same, but could work for some cases: there is another option ALLOWALL
which will effectively remove the restriction, which might be a nice thing for testing/pre-production environments
I had to add X-Frame-Options for IE and Content-Security-Policy for other browsers. So i did something like following.
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
Strictly speaking no, you cant.
You can however specify X-Frame-Options: mysite.com
and therefore allow subdomain1.mysite.com
and subdomain2.mysite.com
. But yes, that's still one domain. There happens to be some workaround for this, but I think it's easiest to read that directly at the RFC specs: https://tools.ietf.org/html/rfc7034
It's also worth to point out that the Content-Security-Policy (CSP) header's frame-ancestor
directive obsoletes X-Frame-Options. Read more here.
One possible workaround would be using a "frame-breaker" script as described here
You just need to alter the "if" statement to check for your allowed domains.
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;
}
This workaround would be safe, I think. because with javascript not enabled you will have no security concern about a malicious website framing your page.
YES. This method allowed multiple domain.
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
'program story' 카테고리의 다른 글
'…'지시문에 필요한 컨트롤러 'ngModel'을 찾을 수 없습니다. (0) | 2020.09.19 |
---|---|
Godaddy에서 발급 한 인증서 사용시 "공개 키 인증서와 개인 키가 일치하지 않습니다." (0) | 2020.09.19 |
YouTube API 버전 3으로 동영상 길이를 어떻게 얻나요? (0) | 2020.09.19 |
실제 사용자 ID, 유효 사용자 ID 및 저장된 사용자 ID의 차이점 (0) | 2020.09.19 |
JavaScript : 값으로 객체를 전달하는 방법? (0) | 2020.09.18 |