如何在Chrome扩展程序中绕过X-Frame-Options DENY?

60

我是Intab的作者,这是一个Chrome扩展程序,它可以让您内联查看链接而不是打开新标签页。幕后并没有太多花哨的东西,只是一个iframe将用户单击的URL加载进来。

它运行良好,但遇到设置X-Frame-Options头为DENY或SAMEORIGIN的网站时就会出问题。一些大型网站,如Google和Facebook都使用了这个选项,这使得体验有些不流畅。

有什么方法可以解决这个问题吗?既然我正在使用Chrome扩展程序,是否有任何浏览器级别的东西可以访问以帮助解决这个问题?期待任何想法或帮助!


3
扩展可能会有所不同,但我知道在JavaScript中目前无法知道页面加载是否被“X-Frame-Options”阻止。在JavaScript中,如果页面加载由于“X-Frame-Options”被阻止,将不会抛出任何错误或触发任何事件。 - G-Nugget
1
我不认为这是可能的。添加X-Frame-Option的原因是为了防止Url被放在不在域中的Iframe中(在同源的情况下)。如果你设法绕过这个限制,那么这就是X-Frame的一个安全漏洞/bug,将在以后的版本中修复。越来越多的网站正在使用此选项来为其网站添加安全性而无需进行大量操作。虽然看到它是否能被打败会很令人兴奋,但这是我的两分钱。 - user428747
6
Chrome扩展应该被允许这样做。它们不是Javascript,它们是“受信任的包”(trusted bundle)的一部分,这意味着它们应该被视为浏览器本身的一部分。 - Pacerier
3个回答

79

此答案适用于ManifestV2和已安装政策的MV3扩展。
对于普通的ManifestV3扩展,请查看其他答案。

Chrome提供了webRequestAPI来拦截和修改HTTP请求。您可以删除X-Frame-Options标头以允许在iframe中内联页面。

chrome.webRequest.onHeadersReceived.addListener(
    function(info) {
        var headers = info.responseHeaders;
        for (var i=headers.length-1; i>=0; --i) {
            var header = headers[i].name.toLowerCase();
            if (header == 'x-frame-options' || header == 'frame-options') {
                headers.splice(i, 1); // Remove header
            }
        }
        return {responseHeaders: headers};
    }, {
        urls: [
            '*://*/*', // Pattern to match all http(s) pages
            // '*://*.example.org/*', // Pattern to match one http(s) site
        ], 
        types: [ 'sub_frame' ]
    }, [
        'blocking',
        'responseHeaders',
        // Modern Chrome needs 'extraHeaders' to see and change this header,
        // so the following code evaluates to 'extraHeaders' only in modern Chrome.
        chrome.webRequest.OnHeadersReceivedOptions.EXTRA_HEADERS,
    ].filter(Boolean)
);

在 manifest 文件中,您需要指定 webRequestwebRequestBlocking 权限,以及您打算拦截的 URL 模式,例如上面的示例中的 "*://*/*""*://www.example.org/*"

4
结果发现content-scripts无法访问webRequest。我需要将其移至后台进行处理。 - Ian McIntyre Silber
2
@IanMcIntyreSilber 你好,我想知道你最终是否解决了这个问题,并且如果你能够的话,是否可以教我一下。 - Zachrip
2
@RobW X-Frame-Options 可以设置为 HTML 元素,请参见:http://javascript.info/tutorial/clickjacking。 - Guy Korland
2
@GuyKorland 我明白了。Chrome扩展程序无法修改响应正文,所以你没有办法。 - Rob W
2
@TaiwanGrapefruitTea 通过扩展程序进行的标题修改不会在开发者工具中显示。如果您想查看标题修改是否成功,请查看chrome://net-internals/#events。 - Rob W
显示剩余28条评论

27

使用declarativeNetRequest的ManifestV3示例

请注意,本答案末尾还有一个警告!

适用于Chrome 96及更高版本的manifest.json
在安装过程中不会显示单独的"阻止页面内容"权限。

  "minimum_chrome_version": "96",
  "permissions": ["declarativeNetRequestWithHostAccess"],
  "host_permissions": ["*://*.example.com/"],
  "background": {"service_worker": "bg.js"},

使用 initiatorDomainsrequestDomains 的 Chrome 101 及更高版本的 bg.js
(不要忘记在 manifest.json 中添加 "minimum_chrome_version": "101"

const iframeHosts = [
  'example.com',
];
chrome.runtime.onInstalled.addListener(() => {
  const RULE = {
    id: 1,
    condition: {
      initiatorDomains: [chrome.runtime.id],
      requestDomains: iframeHosts,
      resourceTypes: ['sub_frame'],
    },
    action: {
      type: 'modifyHeaders',
      responseHeaders: [
        {header: 'X-Frame-Options', operation: 'remove'},
        {header: 'Frame-Options', operation: 'remove'},
        // Uncomment the following line to suppress `frame-ancestors` error
        // {header: 'Content-Security-Policy', operation: 'remove'},
      ],
    },
  };
  chrome.declarativeNetRequest.updateDynamicRules({
    removeRuleIds: [RULE.id],
    addRules: [RULE],
  });
});

旧版Chrome 84-100

如果您的扩展程序需要与这些旧版本兼容,请使用以下内容。

适用于Chrome 84及更高版本的manifest.json
在安装过程中显示了一个单独的权限,用于"阻止页面内容"

  "permissions": ["declarativeNetRequest"],
  "host_permissions": ["*://*.example.com/"],
  "background": {"service_worker": "bg.js"},

使用已弃用的domains的Chrome 84及更高版本的bg.js

const iframeHosts = [
  'example.com',
];
chrome.runtime.onInstalled.addListener(() => {
  chrome.declarativeNetRequest.updateDynamicRules({
    removeRuleIds: iframeHosts.map((h, i) => i + 1),
    addRules: iframeHosts.map((h, i) => ({
      id: i + 1,
      condition: {
        domains: [chrome.runtime.id],
        urlFilter: `||${h}/`,
        resourceTypes: ['sub_frame'],
      },
      action: {
        type: 'modifyHeaders',
        responseHeaders: [
          {header: 'X-Frame-Options', operation: 'remove'},
          {header: 'Frame-Options', operation: 'remove'},
          // Uncomment the following line to suppress `frame-ancestors` error
          // {header: 'Content-Security-Policy', operation: 'remove'},
        ],
      },
    })),
  });
});

警告:请小心网站的服务工作者

在添加iframe或打开扩展页面之前,您可能需要删除网站(s)的服务工作者并清除其缓存,因为许多现代网站使用服务工作者创建页面而无需进行网络请求,从而忽略我们的头部剥离规则。

在manifest.json中的"permissions"中添加"browsingData"。
清除SW: ```javascript function removeSW(url) { return chrome.browsingData.remove({ origins: [new URL(url).origin], }, { cacheStorage: true, serviceWorkers: true, }); } ```
// 如果在DOM中添加iframe元素: ```javascript async function addIframe(url, parent = document.body) { await removeSW(url); const el = document.createElement('iframe'); parent.appendChild(el); el.src = url; return el; } ```
// 如果在扩展页中打开一个带有

我们做了除了清单主机权限之外的所有事情,但它失败了。感谢非常完整的解决方案!此外,对于那些不关心安全的懒惰人来说,这是一个有效的URL模式:“<all_urls>” o.o - Nate
1
附注:对于那些试图使用扩展程序将一个网站的iframe注入到另一个网站中(而不是在扩展程序本身内部)的人,请注意,您需要更改规则的“initiatorDomains”为托管网站的域名,而不是扩展程序本身的域名。 - user7607751

2
您可以尝试使用Frame扩展程序,该扩展程序允许用户删除X-Frame-Options和Content-Security-Policy HTTP响应头,从而允许页面被嵌入到iframe中。
代码可在github上获取。
它基于ManifestV3,并且与Google和Facebook完美配合。

3
您的回答可以通过添加更多支持性信息来改善。请[编辑]以添加进一步细节,比如引用或文献,使得其他人能够确认您的回答正确。您可以在帮助中心找到关于如何撰写好的答案的更多信息。 - Swayangjit

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接