Firefox WebExtension, изолированное наложение HTML

Я ищу способ отображать изолированное наложение на некоторых веб-сайтах с помощью WebExtensions.
Мне кажется, что iframe подходит для этого, поскольку он предоставляет совершенно отдельную область для css, js и DOM. И еще одна интересная вещь заключается в том, что целевой веб-сайт не сможет читать или изменять контент.

В расширениях Chrome это кажется возможным без каких-либо проблем, но с WebExtensions в Firefox, несмотря на то, что они имеют один и тот же синтаксис, я получаю предупреждения/ошибки безопасности, и это не работает.

Я пробовал две разные вещи:

  • Создание iframe без атрибута src и внедрение его в тело веб-сайта. Этот метод не удался, потому что я получаю ошибки/предупреждения CSP при выполнении iframe.contentWindow.document.open().
    Соответствующий код сценария контента:

    let html = `
        <!DOCTYPE html>
        <html>
          <head></head>
          <body>
            <h1>TEST</h1>
          </body>
        </html>
    `
    let iframe = document.createElement('iframe')
    document.body.appendChild(iframe)
    iframe.contentWindow.document.open()
    iframe.contentWindow.document.write(html)
    iframe.contentWindow.document.close()
    
  • Еще одна вещь, которую я пробовал (что имело бы гораздо больше смысла, поскольку это запретило бы веб-сайту доступ к контенту), заключалась в том, чтобы поместить мой код iframe в файл (overlay.html) в моем WebExtension и заставить iframe загрузить его, установив его src на browser.extension.getURL('overlay.html').
    Соответствующий код контента-скрипта:

    let iframe = document.createElement('iframe')
    iframe.src = browser.extension.getURL('overlay.html')
    document.body.appendChild(iframe)
    

    В манифесте я определил overlay.html как web_accessible_resources для этого:

    "web_accessible_resources": [ 
        "overlay.html"
    ],
    

    Дело в том, что iframe просто не загружает файл overlay.html. Но он точно есть, иначе бы location.href = browser.extension.getURL('overlay.html') не работало. Было бы очень удобно, если бы это работало, так как я мог бы сохранить весь оверлей (html, css, js) в виде отдельных файлов в своем расширении. Как будто это будет отдельный сайт. И используя контент-скрипт, я мог бы получить к нему доступ, чтобы добавить новые данные или что-то еще.

Изменить:

На данный момент я использую атрибут srcdoc своего iframe для установки исходного кода, который он должен содержать (спасибо wOxxOm за это). Так что по крайней мере у меня есть что-то, что работает сейчас. Но что мне действительно не нравится в этом подходе, так это то, что я не могу взаимодействовать с содержимым iframe из своего сценария содержимого. Интересно, что я могу взаимодействовать с родительской страницей из js-кода iframe, но опять же не со скриптом контента. Это также очень грязно, неудобно и сложно поддерживать, чтобы поместить весь ваш код html, css, js в одну строку вместо нескольких файлов, как на обычном веб-сайте.


comment
Может быть, сайты, которые вы пробовали, определяют очень ограничительный CSP? Я бы попробовал iframe.srcdoc. Что касается изоляции стилей, вы можете использовать Shadow DOM вместо iframe.   -  person wOxxOm    schedule 19.01.2017
comment
Спасибо, это было невероятно полезно!   -  person Forivin    schedule 20.01.2017
comment
1) О проблеме с CSP: я не думаю, что дело в сайте. У меня такая же ошибка на file:///C:/. Я думаю, что это связано с собственным CSP расширения. Я все еще не знаком с синтаксисом, поэтому не могу предложить решение. Будьте осторожны, выискивая дыры, не полностью понимая, что еще вы можете непреднамеренно допустить. 2) Я создал и сразу же добавил iframe. Прямое присвоение НЕ работает одинаково для всех атрибутов, хотя iframe.src у меня работало. Попробуйте указать ВСЕ атрибуты с помощью iframe.setAttribute( attribute, value ); В частности, я думаю, что class проблематичен.   -  person    schedule 21.01.2017
comment
(продолжение) Однако использование атрибута class, установленного в атрибуте iframe, может привести к риску возникновения проблем с конфликтами классов, которые метод Shadow DOM пытается свести к минимуму. Этот метод является новым для меня, поэтому я не могу комментировать дальше без исследования.   -  person    schedule 21.01.2017
comment
На самом деле, WebExtensions, вероятно, следует полностью избегать iframe. Даже если сейчас сайту доверяют, все, что нужно сделать злоумышленнику, — это ввести код на страницу, и он может скрыть или наложить iframe или изменить атрибут src. bugzilla.mozilla.org/show_bug.cgi?id=1287590 Не уверен, что Тень DOM адекватно защищает от таких вещей.   -  person    schedule 21.01.2017
comment
Не зная ваших точных проблем с безопасностью, я бы поставил на то, что это CSP жалуется на то, что ваш iframe пытается получить доступ к ресурсу через что-то, кроме https.   -  person Daniel Lane    schedule 24.01.2017


Ответы (2)


Проблема

Во-первых, мы знаем, что проблема связана с безопасностью, в чем именно заключается проблема? Что ж, при попытке загрузить ресурс расширения в iframe путем установки iframe src браузер жалуется на безопасность и не позволяет iframe подключаться по другому протоколу, в данном случае «moz-extension://».

Решение

Загрузите html и т. д. из контекста расширения и введите в виде строки.

Основные подробности

Чтобы обойти это, мы можем установить для атрибута src iframe значение data:text/html;charset=utf8,${markup}.

Это напрямую сообщает iframe, что содержимое представляет собой html, оно использует кодировку utf8, а за ним следует необработанная разметка. Мы полностью исключаем необходимость в iframe для загрузки каких-либо ресурсов по сети.

Контекст выполнения скрипта содержимого Firefox отделен от страницы, для которой он был загружен. Это означает, что вы можете сделать запрос xhr, не нарушая CSP.

Если вы делаете запрос xhr к своей разметке, вы можете получить содержимое ответа в виде строки и напрямую вставить его в атрибут iframe src.

Таким образом, сценарий контента:

    function loaded (evt) {
    if (this.readyState === 4 && this.status === 200) {
        var html = this.responseText;
        console.log(html);
        var iframe = document.createElement('iframe');

        iframe.src = 'data:text/html;charset=utf-8,' + html;
        document.body.appendChild(iframe);

        console.log('iframe.contentWindow =', iframe.contentWindow);
    } else {
        console.log('problem loading');
    }
}

var xhr = new XMLHttpRequest();
xhr.overrideMimeType("text/html");
xhr.open("GET", browser.extension.getURL('test.html'), false);
xhr.addEventListener("readystatechange", loaded);


xhr.send(null);

С помощью простого HTML-файла

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <title>Test</title>
    <meta charset="utf8" />
</head>
<body>
   <h1>Hello World</h1>
</body>
</html>

Итак, теперь вы успешно внедрили html-шаблон в целевой iframe.

Если вам нужны какие-либо изображения, скрипты, файлы css и т. д., вам нужно будет написать загрузчик на основе описанного выше метода, внедрив новые теги скрипта и т. д. непосредственно в документ iframe.

person Daniel Lane    schedule 25.01.2017

Кстати, иногда люди используют URL-адрес IFRAME в другом протоколе, например
https:// (в то время как текущая страница http:// ).

Таким образом, URL-адрес IFRAME должен быть относительным, например src="//example.com"

person T.Todua    schedule 28.01.2017
comment
Из контекста расширения firefox, обращающегося к файлам из файловой системы расширений, а не из контекста веб-сайта, это не сработает. файлы расширения firefox находятся в протоколе moz-extension. - person Daniel Lane; 29.01.2017