web-dev-qa-db-fra.com

Injection d'iframe dans la page avec une politique de sécurité de contenu restrictive

Je veux créer une extension de navigateur qui crée une barre latérale. Chrome n'a pas de barre latérale de première classe, et nous devons donc plutôt mettre un iframe dans la page. Cependant, cela se brise sur de nombreuses pages en raison de la politique de sécurité du contenu. Par exemple, GitHub utilise un CSP, ce qui ne permet pas d'y intégrer des iframes provenant d'autres sites. Par exemple, si vous essayez de mettre le site capitalone.com dans un iframe sur GitHub, vous obtenez ce qui suit:

Refus d'encadrer ' https://www.capitalone.com/ ' car il enfreint la directive de politique de sécurité du contenu suivante: "frame-src 'self' render.githubusercontent.com www.youtube.com assets .braintreegateway.com ".

Voici une simple extension de navigateur pour reproduire cela:

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));

Pourtant, selon Wikipedia, une extension de navigateur devrait pouvoir injecter un iframe malgré toute politique de sécurité du contenu:

Selon le modèle de traitement CSP, [20] CSP ne devrait pas interférer avec le fonctionnement des modules complémentaires ou extensions de navigateur installés par l'utilisateur. Cette fonctionnalité de CSP permet efficacement à tout module complémentaire ou extension d'injecter un script dans des sites Web, quelle que soit l'origine de ce script, et donc d'être exempté des politiques CSP.

Y a-t-il une autre manière que je devrais injecter un iframe en plus de ce que je fais?

39
Ben McCann

L'incapacité d'insérer un iframe externe dans Chrome est un bogue ( crbug.com/408932 ).

Si vous souhaitez intégrer un cadre externe dans un site Web externe, il doit être chargé dans un cadre fourni avec votre extension.

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"
    ]
}

Ne pas utiliser chrome.tabs.onUpdated + chrome.tabs.executeScript si vous souhaitez qu'un script de contenu soit toujours inséré dans un document. Votre implémentation est défectueuse et peut entraîner l'exécution du script plusieurs fois. Au lieu de cela, vous devez déclarer le script de contenu dans le fichier manifeste .

(retirer "all_frames": true si vous ne souhaitez pas insérer le cadre dans chaque sous-cadre.)

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>

Il est important de noter que j'ai chargé un site https dans le cadre. Si vous essayez de charger un site HTTP dans le cadre, la stratégie de contenu mixte bloquera le chargement du cadre si l'un des cadres parents est une page https.

Remplacer https://robwu.nl/ avec http://example.com/ et le cadre restera vierge sur les pages https telles que https://github.com . Simultanément, le message suivant sera imprimé sur la console.

[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

La réponse de Rob W est correcte. Vous pouvez suivre cela https://transitory.technology/browser-extensions-and-csp-headers/ . J'ai réussi à le faire fonctionner dans mon Chrome https://github.com/onmyway133/github-chat

Notez que j'utilise Chrome 59, donc je peux utiliser la plupart des fonctionnalités ES6

Déclarez dans le manifeste

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

Créez un iframe dans l'événement window.onload

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

Votre exemple devrait fonctionner dans Chrome, mais il ne fonctionne pas actuellement en raison d'un bogue: https://code.google.com/p/chromium/issues/detail?id=408932 . La réponse de Rob W contient un bon moyen de contourner le problème.

1
Macil