Support for extensions using XUL/XPCOM or the Add-on SDK was removed in Firefox 57, released November 2017. As there is no supported version of Firefox enabling these technologies, this page will be removed by December 2020.

Add-ons using the techniques described in this document are considered a legacy technology in Firefox. Don't use these techniques to develop new add-ons. Use WebExtensions instead. If you maintain an add-on which uses the techniques described here, consider migrating it to use WebExtensions.

Starting from Firefox 53, no new legacy add-ons will be accepted on addons.mozilla.org (AMO) for desktop Firefox and Firefox for Android.

Starting from Firefox 57, only extensions developed using WebExtensions APIs will be supported on Desktop Firefox and Firefox for Android.

Even before Firefox 57, changes coming up in the Firefox platform will break many legacy extensions. These changes include multiprocess Firefox (e10s), sandboxing, and multiple content processes. Legacy extensions that are affected by these changes should migrate to use WebExtensions APIs if they can. See the "Compatibility Milestones" document for more information.

A wiki page containing resources, migration paths, office hours, and more, is available to help developers transition to the new technologies.

Draft
This page is not complete.

This document is intended as a guide for developers to promote best practices in securing your extension. Your goal is to keep your users safe. Some items mentioned are strict guidelines, meaning that if you don't follow them then your add-on will not be approved on Mozilla add-ons. Other items are recommendations. The difference will be clearly flagged.

This is written from the perspective of a Firefox extension, but most items apply to extensions for other Mozilla-based applications such as Thunderbird or SeaMonkey.

Web content handling

In general the best way to ensure that the browser is not compromised when you load content is to make sure it does not have those privileges. A more detailed explanation of this process is in Displaying web content in an extension without security issues.

The privileges that a document gets also depend on where it comes from. For example, if you load a chrome URL, this means the content has been registered with Firefox and has full access. Content from a domain (such as http://www.example.com) can only access that same domain. Files
loaded using the file protocol can access files on the user's disk and other local devices. There are ways to get around the content/chrome security barrier, if for example, you want a web page to send a notification to the add-on (or vice versa). One way to do this is to use custom DOM events; see Interaction between privileged and non-privileged pages.

Regardless of where the document comes from, you can further restrict what it can do by applying properties to the document holder, known as the docshell.

frame.docShell.allowImages = false;
frame.docShell.allowJavascript = false;
frame.docShell.allowPlugins = false;

There are more examples listed in the document listed above in this section. In certain circumstances you may want to run code in your extension, but you would like to give it restricted privileges. One of the best ways to do this is to use Components.utils.evalInSandbox(). Note that objects passed into the sandbox are restricted, but objects that come back out from it are not. Refer to the document to find out how to avoid such pitfalls. For more information, refer to the evalInSandbox section.

The sidebar: a use case

The sidebar in Firefox is designed to hold both chrome (privileged) content and Web (nonprivileged) content, the latter being in the form of Web pages. These Web pages can come from a server, or come from local HTML files bundled with the extension. For pages coming from the server, you need to take steps to ensure that the content can not call back into the Web browser and run malicious code. The main way to do this is by creating an iframe or browser element in the sidebar, and loading your content there. Give the element a type="content" attribute, which essentially sandboxes the code there and blocks callback rights into chrome.

Using eval() in an extension

Using the built-in JavaScript eval function is frowned upon in the context of extensions. While there are some legitimate use-cases, most of the time there are safer alternatives. This blog post offers some excellent reasons not to use eval().

Sandboxed HTTP connections

The main purpose of sandboxed HTTP connections is to interact with a web service, without interfering with the cookies set in the browser by that service/site. For example, if you are loading pictures or other data from a photo sharing site, you can sandbox your connections to that site so that the normal browsing of that site by the user in the main Firefox browser is not affected. For a real world use case, take a look at this blog post.

Handling of logins and passwords

There are various ways of storing data in Firefox, but for user logins and passwords, you should use the Login Manager. This is the same store that holds the logins from web pages, and passwords can only be retrieved using a site/username pairing known to the author. The convention for extensions is to use a chrome url for the site identifier, to avoid clashes with logins for sites. It could be the case that your extension provides a different tool or tools for services on your site.

This approach is preferable because it provides users with a familiar interface for interacting with logins, via the Firefox Password Manager. When users clear logins using the "Clear Recent History" option, it will include your extension's data.

APIs and other data handling

Web content is more than just pages, and more and more add-ons are interacting with web services via an Application Programming Interface (API). Many of the items talked about so far in this document apply in this sphere, but here are some additional tips:

Remote JavaScript and Content

There are a number of ways of remote scripts being used in extensions. They can be included in content, or downloaded at intervals.

Non-chrome urls in chrome XUL or HTML such as the following example are not allowed:

<script type="text/javascript" src="http://mysite.greatsite.com/js/wow-content.js" />

In general, scripts that are from remote sources that run in the chrome context are not acceptable, as many times the source of the script can never be 100% guaranteed, and they are vulnerable to man-in-the-middle attacks. The only legitimate environment for remote scripts is to run in a sandbox. For more information, refer to the evalInSandbox() section.

evalInSandbox

The evalInSandbox document explains very well how it works, so there will be no repetition here. The usefulness of it and power of how it works is best illustrated by the popular Greasemonkey extension, which works on the premise of scripts being downloaded and stored locally, to be injected into the web content context via the sandbox. Many extensions use the Greasemonkey compiler to bundle it in their extension for convenience. If you choose to do so, beware when making edits to the bundled files so as not to break the well thought out security architecture.

Third-party JavaScript

In the context of Web sites, using JavaScript written by others is very common. It is not unheard of in add-ons as well, and can provide a useful way to avoid code duplication and accelerate development. This article is about websites, but gives some insights into the general risks. When you do include other scripts, there are a number of things you can do to ensure their integrity and safety for users. The first is to always get it from a trusted source. Another thing you should do is namespace it just in case other add-ons include it. For example, if you're using jQuery, there's jQuery.noConflict().

Conclusion

Security can't be taken for granted, and for every release of your add-on, a new security audit should take place. A good place to keep up with Mozilla security announcements and security discussion is at the Mozilla Security Blog.