web-dev-qa-db-ja.com

制限的なコンテンツセキュリティポリシーを使用してiframeをページに挿入する

サイドバーを作成するブラウザー拡張機能を作成します。 Chromeにはファーストクラスのサイドバーがないため、代わりにページにiframeを配置する必要があります。ただし、これはコンテンツセキュリティポリシーにより多くのページで中断されます。GitHubはCSPを使用し、たとえば、capitone.com WebサイトをGitHubのiframeに配置しようとすると、次のようになります。

次のコンテンツセキュリティポリシーディレクティブに違反するため、「 https://www.capitalone.com/ 」のフレーム化を拒否しました:「frame-src 'self' render.githubusercontent.com www.youtube.com asset .braintreegateway.com」。

これを再現する簡単なブラウザ拡張機能を次に示します。

chrome.tabs.onUpdated.addListener(function(tabId, changeInfo, tab) {
  if (changeInfo.status === 'complete') {
   chrome.tabs.executeScript(tabId, { code: 'document.body.innerHTML=\'<iframe style=\"width:600px; height:600px\" src=\"https://www.capitalone.com/\"></iframe>\' + document.body.innerHTML;' }, function() {
     console.log('Iframe injection complete');
   })
  }
}.bind(this));

それでも、ウィキペディアによると、ブラウザの拡張機能は、コンテンツセキュリティポリシーに関係なく、iframeを挿入できるはずです。

CSP処理モデルによると、[20] CSPは、ユーザーがインストールしたブラウザーアドオンまたは拡張機能の動作を妨げてはなりません。 CSPのこの機能により、アドオンまたは拡張機能は、そのスクリプトの生成元に関係なく、Webサイトにスクリプトを挿入できるため、CSPポリシーから免除されます。

私がやっていること以外にiframeを注入する他の方法はありますか?

39
Ben McCann

Chromeに外部iframeを挿入できないことはバグです( crbug.com/408932 )。

外部Webサイトに外部フレームを埋め込む場合は、拡張機能とともにパッケージ化されたフレームに読み込む必要があります。

manifest.json

{
    "name": "Embed external site",
    "version": "1",
    "manifest_version": 2,
    "content_scripts": [{
        "js": ["contentscript.js"],
        "matches": ["*://*/*"],
        "all_frames": true
    }],
    "web_accessible_resources": [
        "frame.html"
    ]
}

使ってはいけません chrome.tabs.onUpdated + chrome.tabs.executeScriptコンテンツスクリプトを常にドキュメントに挿入する場合。実装に欠陥があり、スクリプトが複数回実行される可能性があります。代わりに、 マニフェストファイルでコンテンツスクリプトを宣言 する必要があります。

(削除"all_frames": trueフレームをすべてのサブフレームに挿入したくない場合。

contentscript.js

// Avoid recursive frame insertion...
var extensionOrigin = 'chrome-extension://' + chrome.runtime.id;
if (!location.ancestorOrigins.contains(extensionOrigin)) {
    var iframe = document.createElement('iframe');
    // Must be declared at web_accessible_resources in manifest.json
    iframe.src = chrome.runtime.getURL('frame.html');

    // Some styles for a fancy sidebar
    iframe.style.cssText = 'position:fixed;top:0;left:0;display:block;' +
                           'width:300px;height:100%;z-index:1000;';
    document.body.appendChild(iframe);
}

frame.html

<style>
html, body, iframe, h2 {
    margin: 0;
    border: 0;
    padding: 0;
    display: block;
    width: 100vw;
    height: 100vh;
    background: white;
    color: black;
}
h2 {
    height: 50px;
    font-size: 20px;
}
iframe {
    height: calc(100vh - 50px);
}
</style>
<h2>Displaying https://robwu.nl in a frame</h2>
<iframe src="https://robwu.nl/"></iframe>

フレームにhttpsサイトをロードしたことを観察することが重要です。フレームにHTTPサイトをロードしようとした場合、混合コンテンツポリシーは、親フレームの1つがhttpsページである場合、フレームのロードをブロックします。

置換https://robwu.nl/ with http://example.com/https://github.com などのhttpsページではフレームは空白のままになります。同時に、次のメッセージがコンソールに出力されます。

[blocked] The page at 'https://github.com/' was loaded over HTTPS, but ran insecure content from 'http://example.com/': this content should also be loaded over HTTPS

65
Rob W

Rob Wの答えは正しいです。これをフォローできます https://transitory.technology/browser-extensions-and-csp-headers/ 。 Chrome extension https://github.com/onmyway133/github-chat で正常に動作するようにしました

Chrome 59を使用しているため、ほとんどのES6機能を使用できます。

マニフェストで宣言

"web_accessible_resources": [
  "iframe.html",
  "scripts/iframe.js"
]

window.onloadイベントでiframeを作成します

let url    = decodeURIComponent(window.location.search.replace('?url=', ''))
let iframe = document.createElement('iframe')
iframe.src = url

iframe.id = 'github-chat-box-iframe-inner'
iframe.style.width = '100%'
iframe.style.height = '350px'
iframe.style.border = '0px'

window.onload = () => {
  document.body.appendChild(iframe)
}
3
onmyway133

あなたの例はChromeで動作するはずですが、現在のところバグのためではありません: https://code.google.com/p/chromium/issues/detail?id=408932 。 Rob Wの回答には、この問題に対する適切な回避策が含まれています。

1
Macil