Configurable Security Policies
What are Configurable Security Policies?
Mozilla's configurable security policies allow users to set up security policies for the browser, and also have different security policies for different Internet sites. The ideas for configurable security policies come from a number of sources. Bell Labs researchers Vinod Anupam and Alain Mayer have written papers and contributed code to Mozilla. The infamous bug 858 serves as a wish list for this sort of functionality. The code for this is called CAPS (capabilities). Finally, IE's zones employ some of this idea.
This document is aimed at programmers familiar with JavaScript.
N.B. As of Mozilla 0.9.9, a "policynames" line is required in order to create zone policies. See below for details.
Setting Global Policies
Suppose you're annoyed by pop-up advertisements and want to prevent all web pages from opening new browser windows. You can do this by adding the following line to your Mozilla user preferences file (user.js):
user_pref("capability.policy.default.Window.open", "noAccess");
Setting Window.open
to noAccess
means that web pages can
not access the open
property of any object of type Window
.
If a web site tries to open a new window using window.open()
(or open()
), the attempt will fail. The security manager will throw a
JavaScript exception, preventing the function from being called. Unless the
web page catches the exception, the script will stop and an error message
will appear on the JavaScript console (Tasks->Tools->JavaScript Console).
Zone Policies
The default
policy is special; it applies to all sites.
You can also set policies that apply to specific sites or groups of sites,
overriding the default. For example, if you wanted to restrict www.evil.org
and www.annoying.com from creating dialog windows,
you could use the following code:
user_pref("capability.policy.policynames", "strict"); user_pref("capability.policy.strict.sites", "http://www.evil.org http://www.annoying.com"); user_pref("capability.policy.strict.Window.alert", "noAccess"); user_pref("capability.policy.strict.Window.confirm", "noAccess"); user_pref("capability.policy.strict.Window.prompt", "noAccess");
The first line defines the name of the policy or policies you want to create,
in this case "strict"
. If you define more than one policy, list them all
on the same line, like so:
user_pref("capability.policy.policynames", "strict, shoppingsites, ");
The preference "capability.policy.strict.sites
" defines the web
sites to which the strict
policy is applied. The value of that
preference is a list of sites (protocol and hostname only), separated by
spaces. The final three lines define the strict policy. For these sites,
the example above will disallow access to window.alert()
,
window.confirm()
and window.prompt()
.
Note that since we haven't defined whether sites under the strict
policy
can open new windows with window.open()
, the default policy still applies.
Suppose we've also discovered that in blocking access to window.open()
,
we've prevented a script on www.usefulsite.net from working. We can allow this
page to bypass the window.open restriction by setting the Window.open
policy
back to its default value, sameOrigin
:
user_pref("capability.policy.policynames", "trustable"); user_pref("capability.policy.trustable.sites", "http://www.usefulsite.net"); user_pref("capability.policy.trustable.Window.open", "sameOrigin");
The name of the policy can be anything you want; we used strict
and
trustable
in this example, but you could name it blacklist
or
mypolicy
or anything else.
Security Levels
There are three special security levels:
noAccess
: web sites can never access this property or call this function.sameOrigin
(default): web sites can access this property, but only for pages on the same site. See this document for an explanation of how Mozilla determines whether two pages have the same origin.allAccess
: a web site can access this property within the same site and on any other site.
If the security level is not one of the three above, it is treated as a privilege name, and a script can access it only if the script is signed and the user grants the privilege to the script through a dialog.
Get and Set
You can specify a policy that applies only to reading a property, or only
to changing its value, by adding .get
or .set
after the property name.
This allows you to specify one policy for reading a property and another for
changing its value. See below for some examples that block pages
from setting values but not from reading them.
Setting Class.property.get
and Class.property.set
to the same level is
equivalent to setting Class.property
to that level. Don't use get
or
set
after the name of a function (such as open
or write
);
the concept of "get" and "set" applies only to properties which are not functions
(such as bgColor
or location
).
Figuring out Object Names
Figuring out the correct object name to use is sometimes tricky. For example,
suppose you want to prevent a web page from submitting a form for you, but you don't
know the class name for a form element. The easiest way is to find out is to write
a script that converts the object into a string. If you go to a page with a form
and type javascript:alert(document.forms[0])
into the location bar, you'll
see that document.forms[0]
is an [xpconnect wrapped HTMLFormElement]
.
HTMLFormElement
is the name of the class, so you can set
HTMLFormElement.submit
to noAccess
to prevent web pages from
submitting forms using form.submit
.
Some elements, such as HTMLAnchorElement
, have special toString functions
that prevent you from finding their class name easily. If you type
javascript:alert(document.links[0])
in the location bar,
you will see the URL of the first link instead of its class name.
The way to get around this problem is to use the default
toString
function on the object document.links[0], like so:
javascript:alert(window.toString.apply(document.links[0]))
.
Caveat: Some Properties Have Multiple Access Paths
When blocking access to properties, it is important to note that there is more than
one way to access some properties, such as attributes of HTML elements. For example,
suppose a user wants to prevent scripts from www.evil.org
from accessing the
href
attribute of HTML anchor tags (<A HREF="...">)
. The following
prefs are not sufficient:
user_pref("capability.policy.policynames", "nohrefs"); user_pref("capability.policy.nohrefs.sites", "http://www.evil.org"); user_pref("capability.policy.nohrefs.HTMLAnchorElement.href", "noAccess");
While these prefs will prevent a script on www.evil.org
from accessing
document.links[1].href
, the script can access the very same information
using the DOM 2 syntax document.links[1].attributes.getNamedItem("href")
or
document.links[1].getAttribute("href")
. The following prefs will completely
block access to the href attribute:
user_pref("capability.policy.policynames", "nohrefs"); user_pref("capability.policy.nohrefs.sites", "http://www.evil.org"); user_pref("capability.policy.nohrefs.HTMLAnchorElement.href", "noAccess"); user_pref("capability.policy.nohrefs.HTMLAnchorElement.attributes", "noAccess"); user_pref("capability.policy.nohrefs.HTMLAnchorElement.getAttribute", noAccess"); user_pref("capability.policy.nohrefs.HTMLAnchorElement.getAttributeNS", "noAccess");
As a general rule, to block access to an attribute, you must also block the
attributes
property and the getAttribute
and getAttributeNS
methods.
The Complete Preferences Syntax
Here's a more formal statement of the syntax for JavaScript security policies:
- A policy consists of a policynames line, a sites line,
and one or more policy lines.
The sites line must be omitted for the
default
policy, but it must be present for all others. - The policynames line specifies the names of all the policies you want to define.
There should be only one policynames line, no matter how many policies you define. It
has this format:
user_pref("capability.policy.policynames", "<list of policy names>");
where <list of policy names> is a list of the policy names you want to define, separated by commas and/or spaces. - The sites line has this format:
user_pref("capability.policy.<policy name>.sites","<URL list>");
- <policy name> is any combination of letters and numbers, starting with a letter.
- "<URL list> is a list of URLs separated by spaces. Each URL in the list can
either be of the form
protocol:
, which will apply the policy to all URLs with the given protocol (such as http:), orprotocol://host
which will apply to a particular host (for example, http://www.annoyingsite.myisp.com). Don't include the path portion of the URL (the / after the host name or anything after it).
- A policy line has this format:
user_pref("capability.policy.<policy name>.<class name>.<property name>","allAccess | noAccess | sameOrigin | <capability name>");
- <policy name> must be the same as the policy name on the sites line.
- The pref values (
allAccess
, etc.) are described above.
Disabling All Javascript for a Site
The special property javascript.enabled
can be used to disable JavaScript execution,
either globally, using the default
policy, or for a group of sites, using a site policy.
For this special policy, the value of the preference must be "noAccess"
or
"allAccess"
. No other values will work. The following example disables all JavaScript
execution at site1.com and site2.com:
user_pref("capability.policy.policynames", "nojs"); user_pref("capability.policy.nojs.sites", "http://site1.com http://site2.com"); user_pref("capability.policy.nojs.javascript.enabled", "noAccess");
This example disables JavaScript execution at all sites except goodsite.com:
user_pref("capability.policy.policynames", "jsok"); user_pref("capability.policy.default.javascript.enabled", "noAccess"); user_pref("capability.policy.jsok.sites", "http://goodsite.com"); user_pref("capability.policy.jsok.javascript.enabled", "allAccess");
Note that only values of "allAccess"
or "noAccess"
will work for the
javascript.enabled
policy prefs. Don't use "sameOrigin"
or any other
string in this case. Also note that this preference:
user_pref("javascript.enabled", false);
overrides all capability.policy
prefs, including
capability.policy.default.javascript.enabled
, for all sites.
Additional Examples
Prevent web pages from resizing browser windows
user_pref("capability.policy.default.Window.innerWidth.set", "noAccess"); user_pref("capability.policy.default.Window.innerHeight.set", "noAccess"); user_pref("capability.policy.default.Window.outerWidth.set", "noAccess"); user_pref("capability.policy.default.Window.outerHeight.set", "noAccess"); user_pref("capability.policy.default.Window.sizeToContent", "noAccess"); user_pref("capability.policy.default.Window.resizeTo", "noAccess"); user_pref("capability.policy.default.Window.resizeBy", "noAccess");
Prevent web pages from moving browser windows
user_pref("capability.policy.default.Window.screenX.set", "noAccess"); user_pref("capability.policy.default.Window.screenY.set", "noAccess"); user_pref("capability.policy.default.Window.moveTo", "noAccess"); user_pref("capability.policy.default.Window.moveBy", "noAccess");
Prevent web pages from finding the your screen resolution and color settings
(Note: these lines don't block all of the ways a web page might find your screen reslution; they only block the most common ones. They don't prevent a web page from finding out how big its window is.)
user_pref("capability.policy.default.Screen.top", "noAccess"); user_pref("capability.policy.default.Screen.left", "noAccess"); user_pref("capability.policy.default.Screen.width", "noAccess"); user_pref("capability.policy.default.Screen.height", "noAccess"); user_pref("capability.policy.default.Screen.pixelDepth", "noAccess"); user_pref("capability.policy.default.Screen.colorDepth", "noAccess"); user_pref("capability.policy.default.Screen.availWidth", "noAccess"); user_pref("capability.policy.default.Screen.availHeight", "noAccess"); user_pref("capability.policy.default.Screen.availLeft", "noAccess"); user_pref("capability.policy.default.Screen.availTop", "noAccess");
Prevent web pages from changing the text in the status bar
Some web pages create "blind links" by changing the status bar text when you hover over the link, preventing the link address from being show in the status bar. This line will turn most blind links into normal links.
user_pref("capability.policy.default.Window.status", "noAccess");
User Interface
We still have no user interface for configuring security policies. In the future, we hope to have a panel in preferences that allows the user to set policies without having to manually edit user.js or know JavaScript. This may be the hardest part of the feature to implement. See bug 38966.