You are currently viewing a snapshot of www.mozilla.org taken on April 21, 2008. Most of this content is highly out of date (some pages haven't been updated since the project began in 1998) and exists for historical purposes only. If there are any pages on this archive site that you think should be added back to www.mozilla.org, please file a bug.



JavaScript Security in Mozilla

Page Maintained by John Taylor

The component security Terminology page gives a general explaination of many of the terms on this page. This document describes the security model used in Mozilla and provides information on how you can create signed JavaScript applications which can access expanded privileges.

There are two security policies in JavaScript:

  • The same origin policy is the default policy. It dates from Navigator 2.0, and Communicator 4.x.
  • The signed script policy is similar to that of Communicator 4.x. This policy for JavaScript is based upon the Java security model, called object signing. To make use of the new policy in JavaScript, you must use the JavaScript privileges API and then sign your JavaScript scripts.

This document is intended for JavaScript programmers and it contains the following sections:

Same Origin Policy

The same origin policy prevents document or script loaded from one origin, from getting or setting properties from a of a document from a different origin.

Mozilla defines the origin as the substring of a URL that includes protocol://host where host includes the optional :port part. To illustrate, this table gives examples of origin comparisons to the URL http://company.com/dir/page.html.

URL Outcome Reason
http://company.com/dir2/other.html Success
http://company.com/dir/inner/another.html Success
http://www.company.com/dir/other.html Failure Different domain
file://D|/myPage.htm Failure Different protocol
http://company.com:80/dir/etc.html Failure Different port

There is one exception to the same origin rule. A script can set the value of document.domain to a suffix of the current domain. If it does so, the shorter domain is used for subsequent origin checks. For example, assume a script in the document at http://www.company.com/dir/other.html executes this statement:

document.domain = "company.com";

After execution of that statement, the page would pass the origin check with http://company.com/dir/page.html.

However, using the same reasoning, company.com could NOT set document.domain to othercompany.com.

[Return to Top]

Signed Script Policy

Signing scripts involves generating a digital signature and associating that signature with the script it signs. In Communicator 4.x, this association was accomplished by adding the ARCHIVE="..." attribute to a SCRIPT tag to refer to the java archive (JAR) containing the signature for the script. In Mozilla, this association is handled differently. An entire HTML page and any scripts it includes using a <script src="..." tag are signed and placed in a JAR file along with their associated signature. By referring to the HTML page using the jar:http://www.site.com/myjar.jar!/signed.html syntax, the signature is automatically associated with the script, and verified as part of the loading of the page. Special HTML syntax to identify signed scripts (the ARCHIVE and ID attributes) is unnecessary in Mozilla and is no longer recognized. The JavaScript security model for signed scripts is based upon the Java security model for signed objects from Communicator 4.x. By signing a script using a valid certificate issued from a certificate authority (such as VeriSign) you certify that you are the owner of the script and that the script was not modified before reaching the end user. Because signed scripts offer this proof of identity, only signed scripts can be granted extended privileges by the browser. Using this model you can sign any JavaScript in an HTML page or referred to by the HTML page.

A signed script requests expanded privileges, gaining access to restricted information. You can use these expanded privileges to exercise fine-grained control over activities beyond those which are normally allowed to JavaScript.

All access-control decisions boil down to who is allowed to do what. In this model, a principal represents the "who," the target represents the "what" and the privilege associated with a principal represents the authorization (or denial of authorization) for code signed by a principal to access a specific privilege.

Once you have written the script, you sign it using Netscape's SignTool. SignTool associates a digital signature with HTML and JS files. That digital signature is owned by a particular principal (a real-world entity such as Netscape or John Smith). The digital signature and the files it signs are both placed in a Java Archive (JAR) file.

The associated principal allows the user to confirm the identity of the entity which signed the script. It also allows the user to ensure that the script hasn't been tampered with since it was signed. The user then can decide whether to grant privileges based on the validated identity of the certificate owner and integrity of the script.

You should always keep in mind that a user may deny the privileges requested by your script. You should write your scripts to react gracefully to such decisions. See the exception handling section.

This document assumes that you are already familiar with the basic principals of object signing, using the JavaScript API and creating digital signatures. The following documents provide information on these subjects:

Object Signing References

Codebase Principals

JavaScript supports codebase principals. A codebase principal is a principal derived from the origin of the script rather than from verifying a digital signature of a certificate. Since codebase principals offer weaker security, they are disabled by default in Mozilla. Codebase principals do not offer as strong a proof of identity, thus end users are unable to make informed choices on whether to grant the script extended privileges.

To enable codebase principals, end users must add this line to the Mozilla prefs.js file:

user_pref("signed.applets.codebase_principal_support", true);

For deployment, your scripts should not rely on codebase principals being enabled. You might want to enable codebase principals when developing your scripts, but you should sign them before delivery.

Even when codebase principals are disabled, Mozilla keeps track of codebase principals to use in enforcement of the same origin security policy, described in "Same Origin Policy". Unsigned scripts have an associated set of principals that contains a single element, the codebase principal for the page containing the script. Signed scripts also have codebase principals in addition to the stronger certificate principals.

With codebase principals enabled, when the user accesses the script, a dialog displays similar to the one displayed with signed scripts. The difference is that this dialog asks the user to grant privileges based on the URL and doesn't provide author verification.

Scripts Signed by Different Principals

JavaScript differs from Java in several important ways that relate to security. Java signs classes and is able to protect internal methods of those classes through the public/private/protected mechanism. Marking a method as protected or private immediately protects it from an attacker. In addition, any class or method marked final in Java cannot be extended and so is protected from an attacker.

On the other hand, because JavaScript has no concept of public and private methods, there are no internal methods that could be protected by simply signing a class. In addition, all methods can be changed at runtime, so must be protected at runtime.

In JavaScript you can add new properties to existing objects, or replace existing properties (including methods) at runtime. You cannot do this in Java. So, once again, protection that is automatic in Java must be handled separately in JavaScript.

While the signed script security model for JavaScript is based on the object signing model for Java, these differences in the languages mean that when JavaScript scripts produced by different principals interact, it is much harder to protect the scripts. Because all of the JavaScript code on a single HTML page runs in the same process, different scripts on the same page can change each other's behavior. For example, a script might redefine a function defined by an earlier script on the same page.

To ensure security, the basic assumption of the JavaScript signed script security model is that mixed scripts on an HTML page operate as if they were all signed by the intersection of the principals that signed each script. This is very important in Mozilla. If you have a web page with signed and unsigned code, the entire page will be regarded as unsigned. In addition, only one signature should be assigned to each JAR file. Mozilla does not currently support multiple signatures.

[Return to Top]

Using Expanded Privileges

Mozilla uses the JavaScript privileges API.

In the simplest case, you add one line of code asking permission to enable a privilege which allows a script to access a target. For example:

netscape.security.PrivilegeManager.enablePrivilege("UniversalPreferencesRead")
or
netscape.security.PrivilegeManager.enablePrivilege("UniversalPreferencesWrite")

When the script calls this function, if the signature is valid or codebase principal are enabled, expanded privileges can be granted. If a user has not accessed this principal before, a dialog asks the user if he wants to accept the signed code. Unlike Communicator 4.x, Mozilla does not display a detailed Java grant dialog, rather a simple dialog asking if the principal can be trusted. The user can accept or deny and allow their choice to be remembered by the browser. As shown in the second example, two privileges may be asked for at once, so only one dialog appears.

Privileges are granted only in the scope of the requesting function. This scope includes any functions called by the requesting function. When the script leaves the requesting function, privileges no longer apply.

The following example demonstrates this by printing:

7: disabled
5: disabled
2: disabled
3: enabled
1: enabled
4: enabled
6: disabled
8: disabled

Function g requests expanded privileges, and only the commands and functions called after the request and within function g are granted privileges. It is good practice to enable privileges only when needed, then disable the privilege soon after the code is executed. This will help protect against potentially dangerous sections of code being run on the user's computer.

<script type="text/javascript">
function printEnabled(i) {
 if (history[0] == "") {
 document.write(i + ": disabled<br>");
 } else {
 document.write(i + ": enabled<br>");
 }
}

function f() {
 printEnabled(1);
}

function g() {
 printEnabled(2);
 netscape.security.PrivilegeManager.enablePrivilege("UniversalBrowserRead");
 printEnabled(3);
 f();
 printEnabled(4);
}

function h() {
 printEnabled(5);
 g();
 printEnabled(6);
}

printEnabled(7);
h();
printEnabled(8);

</script>

Privileges

Privilege represents permissions to access a specific target. The following table lists JavaScript built in privilege and the targets associated with them.

Privileges Targets
UniversalBrowserRead Reading of sensitive browser data.
This allows the script to pass the same origin check when reading from any document.
UniversalBrowserWrite Modification of sensitive browser data.
This allows the script to pass the same origin check when writing to any document.
UniversalXPConnect Unrestricted access to browser APIs using XPConnect
UniversalPreferencesRead Read preferences using the navigator.preference method.
UniversalPreferencesWrite Set preferences using the navigator.preference method.
UniversalFileRead Access to file:// URLs.

In addition, any DOM propertycan be associated with a privilege (user defined) as discussed in the "Configurable Security" section.

JavaScript Features Requiring Privileges

This section lists the JavaScript features that require expanded privileges and the target used to access each feature. Unsigned scripts cannot use any of these features, unless the end user has enabled codebase principals.

  • Using an about: URL other than about:blank requires UniversalBrowserRead.
  • history object: Getting the value of any property requires UniversalBrowserRead.
  • navigator object:
    • Getting the value of a preference using the preference method requires UniversalPreferencesRead.
    • Setting the value of a preference using the preference method requires UniversalPreferencesWrite.
  • window object: All of the following operations require UniversalBrowserWrite.
    • Adding or removing the directory bar (or personal bar), location bar, menu bar, scroll bar, status bar or (navigation) toolbar.
    • Using the methods in the following table under the indicated circumstances
    • enableExternalCapture To capture events in pages loaded from different servers. Follow this method with captureEvents.
      close To unconditionally close a browser window.
      moveBy To move a window off screen.
      moveTo To move a window off screen.
      open
      • To create a window smaller than 100 x 100 pixels or larger than the screen can accommodate by using innerWidth, innerHeight, outerWidth, and outerHeight.
      • To place a window off screen by using screenX and screenY.
      • To create a window without a title bar by using titlebar.
      • To use alwaysRaised, alwaysLowered, or z-lock for any setting.
      resizeTo To resize a window smaller than 100 x 100 pixels or larger than the screen can accommodate.
      resizeBy To resize a window smaller than 100 x 100 pixels or larger than the screen can accommodate.
    • Setting the properties in the following table under the indicated circumstances:
    • innerWidth To set the inner width of a window to a size smaller than 100 x 100 or larger than the screen can accommodate.
      innerHeight To set the inner height of a window to a size smaller than 100 x 100 or larger than the screen can accommodate.

Example

The following script includes a button, that, when clicked, displays an alert dialog containing part of the URL history of the browser. To work properly, the script must be signed.

<script type="text/javascript">

function getHistory(i) {
 //Attempt to access privileged information
 return history[i];
}

function getImmediateHistory() {
 //Request privilege
 netscape.security.PrivilegeManager.enablePrivilege("UniversalBrowserRead");
 return getHistory(1);
}

</script>

...

<input type="button" onclick="alert(getImmediateHistory());" id="b">

[Return to Top]

Writing the Script

This section describes special considerations for writing signed scripts.

International Characters in Signed Scripts

When used in scripts, international characters can appear in string constants and in comments. JavaScript keywords and variables cannot include special international characters.

Scripts that include international characters cannot be signed because the process of transforming the characters to the local character set invalidates the signature. To work around this limitation:

  • Escape the international characters ('0x\ea', and so on).
  • Put the data containing the international characters in a hidden form element, and access the form element through the signed script.
  • Separate signed and unsigned scripts into different layers, and use the international characters in the unsigned scripts.
  • Remove comments that include international characters.

Hints for Writing Secure JavaScript

Check the Location of the Script

If you have signed scripts in pages you have posted to your site, it is possible to copy the JAR file from your site and post it on another site. As long as the signed scripts themselves are not altered, the scripts will continue to operate under your signature. (See "Debugging Invalid Signature Errors" for one exception to this rule.)

If you wish to prevent this, you can force your scripts to work only from your site.

<script type="text/javascript">
if (document.URL.match(/^http:\/\/www.company.com\//)) {
netscape.security.PrivilegeManager.enablePrivilege(...);
// Do your stuff
}
</script>

Then if the JAR file and script are copied to another site, they no longer work. If the person who copies the script alters it to bypass the check on the source of the script, the signature is invalidated.

Minimize the Trusted Code Base

In security parlance, the Trusted Code Base (TCB) is the set of code that has privileges to perform restricted actions. One way to improve security is reduce the size of the TCB, which then gives fewer points for attack or opportunities for mistakes.

For example, the following code, if executed in a signed script with the user's approval, opens a new window containing the history of the browser:

<script type="text/javascript">
netscape.security.PrivilegeManager.enablePrivilege("UniversalBrowserAccess");
var win = window.open();
for (var i=0; i < history.length; i++) {
 win.document.writeln(history[i] + "<br>");
}
win.close();
</script>

The TCB in this instance is the entire script because privileges are acquired at the beginning and never reverted. You could reduce the TCB by rewriting the program as follows:

<script type="text/javascript">
var win = window.open();
netscape.security.PrivilegeManager.enablePrivilege("UniversalBrowserAccess");
for (var i=0; i < history.length; i++) {
 win.document.writeln(history[i] + "<br>");
}
netscape.security.PrivilegeManager.revertPrivilege("UniversalBrowserAccess");
win.close();
</script>

With this change, the TCB becomes only the loop containing the accesses to the history property. You could avoid the extra call to revert the privilege by introducing a function:

<script type="text/javascript">
function writeArray() {
 netscape.security.PrivilegeManager.enablePrivilege("UniversalBrowserAccess");
 for (var i=0; i < history.length; i++) {
 win.document.writeln(history[i] + "<br>");
 }
}
var win = window.open();
writeArray();
win.close();
</script>

The privileges are automatically reverted when writeArray returns, so you don't have to do so explicitly.

[Return to Top]

Signing Scripts

During development of a script you'll eventually sign, you can use codebase principals for testing, as described in "Codebase Principals". Once you've finished modifying the script, you need to sign it. The major difference in signing scripts between 4.x and Mozilla is that in Mozilla, the entire page must be signed, as opposed to only the script running on the page. For any script to be granted expanded privileges, all scripts on or included by an HTML page must be signed.

You can sign JavaScript files (accessed with the src attribute of the script tag), inline scripts, event handler scripts JavaScript entities and javascript: URLs.

Using SignTool

Use SignTool to sign scripts. SignTool is a program that signs scripts and HTML files, and packages them in a JAR file with the signature.

The signtool program extracts scripts from HTML files, signs them, and places their digital signatures in the archive specified in the command line. It also takes care of copying external JavaScript files loaded by the src attribute of the script tag. The script tags in the HTML pages can specify more than one JAR file; if so, signtool creates as many JAR files as it needs.

For information on using this tool, see Using SignTool.

Here is an example of the syntax needed for signing scripts

% signtool -k"Cert Name" -Z"secure.jar" secure-files/

This command will create a JAR file (secure.jar) signed by "Cert Name". All the JavaScript and HTML files in the directory secure-files/ will be signed and stored in the JAR file.

After Signing

Once you've signed a script, any time you change it you must resign it. For JavaScript files, this means you cannot change anything in the file. A change can be as simple as adding or removing white space in the script.

For testing, use SignTool to create a test certificate (see documentation). However, end users will not be able to use the test certificate, so remember to obtain a certificate from a certificate authority in order to serve a signed script on the web.

Accessing a Signed Page

New in Mozilla is the syntax needed to access signed scripts within JAR files. The syntax is as follows:

jar:http://www.domain.com/secure-scripts/secure.jar!/thepage.html

Scripts will only be treated as signed if the HTML page that contains them is using a URL of this form. Changes to a signed script's byte stream invalidate the script's signature. This includes moving the HTML page between platforms that have different representations of text. For example, moving an HTML page from a Windows server to a UNIX server changes the byte stream and invalidates the signature. (This doesn't affect viewing pages from multiple platforms.) To avoid this, you can move the page in binary mode. Note that doing so changes the appearance of the page in your text editor but not in the browser.

[Return to Top]

Troubleshooting Signed Scripts

Exception Handling

Exception handling is highly recommended when using signed scripts. It allows you to deal gracefully with errors or the user choosing to deny a privilege.

For example:

<script type="text/javascript">
function getPriv() {
try {
netscape.security.PrivilegeManager.enablePrivilege("UniversalPreferencesRead");
} catch (err) {
document.write("Sorry, you can not enjoy this site because of " +err+ ".");
return false;
}
document.write("Thanks, privileges accepted");

//... Do stuff

return true;
}
</script>

If the user denies the privilege, an exception will be thrown and the program will immediately execute the code in the catch section. If there is no catch clause, the script will end abroptly, and an error will be shown in the JavaScript console.

Errors on the JavaScript Console

Be sure to check the JavaScript console for errors if your signed scripts do not function as expected. Remember, if you are using exception handling, you will not see the errors in the JS Console. You may see errors such as the following:

# Signature Verification Error: the signature on
# securitycheck.jar is invalid because
# the archive did not contain a valid PKCS7 signature.

The path value printed for signed JavaScript is either the value of the ID attribute or the src attribute of the tag that supplied the script.

Debugging Invalid Signature Errors

Invalid signature errors occur if the script has changed from when it was signed. The most common cause of this problem is that the scripts have been moved from one platform to another with a text transfer rather than a binary transfer. Because line separator characters can differ from platform to platform, the hash could change from when the script was originally signed.

"User did not grant privilege" Exception or Unsigned Script Dialog

Depending on whether or not you have enabled codebase principals, you see different behavior if a script attempts to enable privileges when it isn't signed or when its principals have been downgraded due to mixing of signed and unsigned scripts.

If you have not enabled codebase principals and an unsigned script attempts to enable privileges, it gets an exception from Java that the "Enable privilege not granted". If you did enable codebase principals, you will see a security dialog asking for permissions for the unsigned code.

[Return to Top]

Communicator 4.x links

[Return to Top]