Safely inserting external content into a page

There are times when you might want or need to include content from an external source in your extension. But, there is the risk that the source may have malicious scripts embedded in it—added by either the developer of the source or by a malicious third-party.

Take an RSS reader as an example. You don’t know what RSS feeds your extension will open and have no control over the content of those RSS feeds. So, it’s possible the user could subscribe to a feed where, for example, a feed item’s title includes a script. This could be something as simple as including JavaScript code within <script></script> tags. If you were to extract the title, assume it was plain text, and add it to the DOM of a page created by your extension, your user now has an unknown script running in their browser. Therefore, care needs to be taken to avoid evaluating arbitrary text as HTML.

You also need to remember that extensions have privileged contexts, for example in background scripts and content scripts. In the worst case, an embedded script could run in one of these contexts, a situation known as privilege escalation. This situation can leave a user’s browser open to remote attack by enabling the website that injected the code to access critical user data, such as passwords, browser history, or browsing behavior.

This article examines how to work safely with remote data and add it to a DOM.

Working with arbitrary strings

When working with strings, there are a couple of recommended options to safely add them to a page: the standard DOM node creation methods or jQuery.

DOM node creation methods

A lightweight approach to inserting strings into a page is to use the native DOM manipulation methods: document.createElement , Element.setAttribute ,和 Node.textContent . The safe approach is to create the nodes separately and assign their content using textContent:

var data = JSON.parse(responseText);
var div = document.createElement("div");
div.className = data.className;
div.textContent = "Your favorite color is now " + data.color;
addonElement.appendChild(div);

					

This approach is safe because the use of .textContent automatically escapes any remote HTML in data.color .

However, beware, you can use native methods that aren’t safe. Take the following code:

var data = JSON.parse(responseText);
addonElement.innerHTML = "<div class='" + data.className + "'>" +
                         "Your favorite color is now " + data.color +
                         "</div>";

					

Here, the contents of data.className or data.color could contain HTML that can close the tag early, insert arbitrary further HTML content, then open another tag.

jQuery

When using jQuery, functions such as attr() and text() escape content as it’s added to a DOM. So, the “favorite color” example from above, implemented in jQuery, would look like this:

var node = $("</div>");
node.addClass(data.className);
node.text("Your favorite color is now " + data.color);

					

Working with HTML content

When working with externally sourced content that you know is HTML, sanitizing the HTML is essential before it’s added to a page. Best practice for sanitizing HTML is to use an HTML sanitization library or a template engine with HTML sanitization features. In this section, we look at some suitable tools and how to use them.

HTML sanitization

An HTML sanitization library strips anything that could lead to script execution from HTML, so you can safely inject complete sets of HTML nodes from a remote source into your DOM. DOMPurify , which has been reviewed by various security experts, is a suitable library for this task in extensions.

For production use, DOMPurify comes as a minified version: purify.min.js. You can use this script in the way that best suits your extension. For example, you could add it as a content script:

"content_scripts": [
  {
    "matches" : ["<all_urls>"],
    "js": ["purify.min.js", "myinjectionscript.js"]
  }
]

					

Then, in myinjectionscript.js you can read the external HTML, sanitize it, and add it to a page’s DOM:

var elem = document.createElement("div");
var cleanHTML = DOMPurify.sanitize(externalHTML);
elem.innerHTML = cleanHTML;

					

You can use any method to add the sanitized HTML to your DOM, for example jQuery’s .html() function. Remember though that the SAFE_FOR_JQUERY flag needs to be used in this case:

var elem = $("<div/>");
var cleanHTML = DOMPurify.sanitize(externalHTML, { SAFE_FOR_JQUERY: true });
elem.html(cleanHTML);

					

Template engines

Another common pattern is to create a local HTML template for a page and use remote values to fill in the blanks. While this approach is generally acceptable, care should be taken to avoid use of constructs that would allow the insertion of executable code. This can happen when the templating engine uses constructs that insert raw HTML into the document. If the variable used to insert raw HTML is of a remote source, it is subject to the same security risk mentioned in the introduction.

For example, when using mustache templates you must use the double mustache, {{variable}} , which escapes any HTML. Use of the triple mustache, {{{variable}}} , must be avoided as this injects a raw HTML string and could add executable code to your template. Handlebars works in a similar way, with variables in double handlebars, {{variable}} , being escaped. Whereas, variables in triple handlebars are left raw and must be avoided. Also, if you create a Handlebars helper using Handlebars.SafeString 使用 Handlebars.escapeExpression() to escape any dynamic parameters passed to the helper. This is a requirement because the resulting variable from Handlebars.SafeString is considered safe and it isn’t escaped when inserted with double handlebars.

There are similar constructs in other templating systems that need to be approached with the same level of care.

Additional reading

For more information on this subject, see the following articles:

Found a problem with this page?

最后修改: , 由 MDN 贡献者

  1. 浏览器扩展名
  2. 快速入门
    1. What are extensions?
    2. Your first extension
    3. Your second extension
    4. Anatomy of an extension
    5. Example extensions
    6. What next?
  3. 概念
    1. Using the JavaScript APIs
    2. Content scripts
    3. Match patterns
    4. Working with files
    5. 国际化
    6. Content Security Policy
    7. Native messaging
    8. Differences between API implementations
    9. Chrome incompatibilities
  4. 用户界面
    1. 用户界面
    2. Toolbar button
    3. Address bar button
    4. Sidebars
    5. Context menu items
    6. Options page
    7. Extension pages
    8. Notifications
    9. Address bar suggestions
    10. Developer tools panels
  5. 如何
    1. Intercept HTTP requests
    2. Modify a web page
    3. Insert external content
    4. Share objects with page scripts
    5. Add a button to the toolbar
    6. Implement a settings page
    7. Work with the Tabs API
    8. Work with the Bookmarks API
    9. Work with the Cookies API
    10. Work with contextual identities
    11. Interact with the clipboard
    12. Build a cross-browser extension
  6. Firefox differentiators
  7. JavaScript API
    1. Browser support for JavaScript APIs
    2. alarms
    3. bookmarks
    4. browserAction
    5. browserSettings
    6. browsingData
    7. captivePortal
    8. clipboard
    9. 命令
    10. contentScripts
    11. contextualIdentities
    12. Cookie
    13. devtools
    14. dns
    15. downloads
    16. events
    17. extension
    18. extensionTypes
    19. find
    20. history
    21. i18n
    22. identity
    23. idle
    24. management
    25. menus
    26. notifications
    27. omnibox
    28. pageAction
    29. permissions
    30. pkcs11
    31. privacy
    32. proxy
    33. runtime
    34. search
    35. sessions
    36. sidebarAction
    37. storage
    38. tabs
    39. theme
    40. topSites
    41. 类型
    42. userScripts
    43. webNavigation
    44. webRequest
    45. windows
  8. Manifest keys
    1. 介绍
    1. 作者
    2. background
    3. browser_action
    4. browser_specific_settings
    5. chrome_settings_overrides
    6. chrome_url_overrides
    7. 命令
    8. content_scripts
    9. content_security_policy
    10. default_locale
    11. description
    12. developer
    13. devtools_page
    14. dictionaries
    15. externally_connectable
    16. homepage_url
    17. icons
    18. incognito
    19. manifest_version
    20. name
    21. offline_enabled
    22. omnibox
    23. optional_permissions
    24. options_page
    25. options_ui
    26. page_action
    27. permissions
    28. protocol_handlers
    29. short_name
    30. sidebar_action
    31. storage
    32. theme
    33. theme_experiment
    34. user_scripts
    35. version
    36. version_name
    37. web_accessible_resources
  9. Extension Workshop
    1. Develop
    2. Publish
    3. Manage
    4. Enterprise
  10. Contact us
  11. Channels
    1. Add-ons blog
    2. Add-ons forum
    3. Add-ons chat