Signed Scripts in Mozilla
Page Maintained by Jesse Ruderman
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. The component security Terminology page gives a general explaination of many of the terms on this page.
Contents
- Signed Script Policy
- Using Expanded Privileges
- Writing the Script
- Signing Scripts
- Troubleshooting Signed Scripts
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 one SCRIPT tag to refer to the JAR archive containing the signatures for each of the scripts in the page. 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 user. Using this model you can sign any JavaScript in an HTML page or referred to by the HTML page with <SCRIPT SRC="...">.
A signed script can request expanded privileges that give it access to restricted information and abilities. 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 Mozilla's SignTool, which is one of the NSS tools. 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:
|
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 prefs.js file in their Mozilla user profile directory:
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 they 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.
Using Expanded Privileges
Mozilla uses a set of functions for JavaScript security similar to those used for Java and JavaScript security in Netscape Communicator 4.x.
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("UniversalPreferencesRead 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.
Example
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>
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>
The example prints the following output:
7: disabled 5: disabled 2: disabled 3: enabled 1: enabled 4: enabled 6: disabled 8: disabled
Privileges
Privilege represents permissions to access a specific target. The following table lists JavaScript built in privilege and the targets associated with them.
|
In addition, any DOM property can 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.
- Using the history object to find out what other sites the user has visited, or how many other sites the user has visited in this session, 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, location bar, menu bar, personal bar, scroll bar, status bar, or toolbar.
- Using the methods in the following table under the indicated circumstances
- 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=no.
- To use alwaysRaised, alwaysLowered, or z-lock for any setting.
- Setting the properties in the following table under the indicated circumstances:
| ||||||||||
|
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> 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">
Writing the Script
This section describes special considerations for writing signed scripts.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> if (location.href.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> 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> 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> 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.
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.
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. It can be download from ftp.mozilla.org as part of the NSS package. You'll also need to download NSPR.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
|
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.
CHANGE NOTE: Browsers based on Mozilla code version 1.8.1.10 or later
(e.g. Firefox 2.0.0.10) will not open files using the jar: URI scheme unless
the server sends a Content-Type
header that indicates the archive
is safe to be treated as active code. You must configure your server to send
application/java-archive
for these files (application/x-jar
is also supported).
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.
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> 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.
Communicator 4.x links
- For more tips on writing your scripts, see Danny Goodman's View Source article, Applying Signed Scripts.