我有一个asp.net 4.0 IIS7.5站点,我需要使用x-frame header选项保护
我还需要启用我的网站页面来自我的同一个域以及我的Facebook应用程序。
目前,我的网站配置了以:
为首的网站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
作为新功能似乎存在根本缺陷。
答案 0 :(得分:76)
X-Frame-Options
已弃用。来自MDN:
此功能已从Web标准中删除。虽然有些浏览器可能仍然支持它,但它正在被删除。不要在旧项目或新项目中使用它。使用它的页面或Web应用程序可能随时中断。
现代替代方案是Content-Security-Policy
标头,与许多其他政策一起使用frame-ancestors
指令可以列出允许在框架中托管您网页的网址。
frame-ancestors
支持多个域甚至通配符,例如:
Content-Security-Policy: frame-ancestors 'self' example.com *.example.net ;
不幸的是,目前,Internet Explorer does not fully support Content-Security-Policy。
更新: MDN已删除弃用评论。以下是来自W3C's Content Security Policy Level
的类似评论
frame-ancestors
指令废弃X-Frame-Options
标头。如果资源同时具有两个策略,则应该强制执行frame-ancestors
策略,并且应该忽略X-Frame-Options
策略。
答案 1 :(得分:33)
来自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(以及所有基于webkit的浏览器)does not support ALLOW-FROM
语句。
答案 2 :(得分:7)
一种方法如何不仅允许多个域,而且允许动态域。
这里的用例是一个Sharepoint应用程序部分,它通过iframe将我们的站点加载到Sharepoint内部。问题是sharepoint具有动态子域,例如https://yoursite.sharepoint.com。所以对于IE,我们需要指定ALLOW-FROM https://.sharepoint.com
棘手的事业,但我们可以通过了解两个事实来完成它:
加载iframe时,它仅验证第一个请求中的X-Frame-Options。加载iframe后,您可以在iframe中导航,并且不会在后续请求中检查标题。
此外,加载iframe时,HTTP referer是父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结尾,以确保我们的网站免受点击劫持。
我很乐意听到有关此方法的反馈。
答案 3 :(得分:5)
Necromancing。
提供的答案不完整。
首先,如前所述,您无法添加多个不允许来自的主机。
其次,您需要从HTTP引用来动态提取该值,这意味着您无法将值添加到Web.config,因为它并不总是相同的值。
有必要进行浏览器检测以避免在浏览器为Chrome时添加allow-from(它会在调试控制台上产生错误,这可能会快速填满控制台,或者使应用程序变慢)。这也意味着您需要修改ASP.NET浏览器检测,因为它错误地将Edge标识为Chrome。
这可以在ASP.NET中通过编写一个在每个请求上运行的HTTP模块来完成,该模块为每个响应附加一个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)
{
}
}
}
如果您不拥有应用程序源代码,则可以向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而不是C#编写程序集,VB会为每个项目创建一个default-Namespace,因此您的字符串将如下所示
"[DefaultNameSpace.Namespace].Class, Assembly"
如果要避免此问题,请在C#中编写DLL。
答案 4 :(得分:1)
HTTP Header Field X-Frame-Options的RFC指出X-Frame-Options标头值中的“ ALLOW-FROM”字段只能包含一个域。不允许使用多个域。
RFC建议解决此问题。解决方案是在iframe src url中将域名指定为url参数。然后,托管iframe src url的服务器可以检查url参数中指定的域名。如果域名与有效域名列表匹配,则服务器可以发送X-Frame-Options标头,其值是:“ ALLOW-FROM domain-name”,其中domain name是尝试访问的域名。嵌入远程内容。如果未提供域名或该域名无效,则可以发送X-Frame-Options标头,其值是:“ deny”。
答案 5 :(得分:1)
严格来说,不能。
不过,您可以指定X-Frame-Options: mysite.com
,因此允许subdomain1.mysite.com
和subdomain2.mysite.com
。但是,是的,这仍然是一个领域。碰巧有一些解决方法,但是我认为最容易按照RFC规范进行阅读:https://tools.ietf.org/html/rfc7034
还值得指出的是,Content-Security-Policy(CSP)标头的frame-ancestor
伪指令已淘汰X-Frame-Options。 Read more here。
答案 6 :(得分:0)
不完全相同,但可以适用于某些情况:还有另一个选项ALLOWALL
可以有效地消除限制,这对于测试/预生产环境来说可能是一件好事
答案 7 :(得分:0)
根据MDN Specifications,Chrome不支持X-Frame-Options: ALLOW-FROM
,Edge和Opera中的支持功能未知。
Content-Security-Policy: frame-ancestors
会覆盖X-Frame-Options
(根据this W3 spec),但frame-ancestors
的兼容性有限。根据这些MDN Specs,它在IE或Edge中不受支持。
答案 8 :(得分: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
答案 9 :(得分:-2)
一种可能的解决方法是使用here
所述的“帧断路器”脚本您只需要更改“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,您将无法对构建网页的恶意网站进行安全保护。
答案 10 :(得分:-5)
YES。此方法允许多个域。
VB.NET
response.headers.add("X-Frame-Options", "ALLOW-FROM " & request.urlreferer.tostring())