Javascript Security in Communicator 4.X: Same Origin Policy
Javascript Security in Communicator 4.X: Same Origin Policy
This document describes the security model used by JavaScript in Communicator 4.x andprovidesinformationonhowyoucanusethenewsecurityfeaturestocreatesigned JavaScript scripts. There are two security policies in JavaScript: The same origin policy is the default policy. It dates from Navigator 2.0, with necessary coverage fixes in Navigator 2.01 and Navigator 2.02. ThesignedscriptpolicyisnewtoCommunicator 4.0.ThisnewpolicyforJavaScriptis baseduponthenewJavasecuritymodel,calledobjectsigning.Tomakeuseofthenew policy in JavaScript, you must use the new Java security classes and then sign your JavaScript scripts. This document is intended for JavaScript programmers. Its contains the following sections: Same Origin Policy Signed Script Policy Identifying Signed Scripts Using Expanded Privileges Writing the Script Signing Scripts Troubleshooting Signed Scripts
Here, Communicator defines the origin as the substring of a URL that includes protocol://host where host includes the optional :port part. To illustrate, Table 1 gives examples of origin comparisons to the URL http:// company.com/dir/page.html.
Table 1 Same origin comparisons URL
http://company.com/dir2/other.html http://company.com/dir/inner/another.html http://www.company.com/dir/other.html file://D|/myPage.htm http://company.com:80/dir/etc.html
Reason
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 withhttp://
company.com/dir/page.html.
Table 2 lists the properties that can be accessed only by scripts that pass the same origin check.
Table 2 Properties subject to origin check Object
image layer location
Properties
find
For both read and write: anchors, applets, cookie, domain, elements, embeds, forms, lastModified, length, links, referrer, title, URL, formName (for each named form), reflectedJavaClass (for each Java class reflected into JavaScript using LiveConnect) For write only: all other properties
Named forms
In Navigator 3.0, named forms were not subject to an origin check even though the document.forms array was.In Communicator 4.0, named forms are alsosubject to an origin check. This can cause existing code to break. You can easilywork around the resulting security errors. To do so, createa newvariable as a property of the window object, setting the named form as the value of the variable. You can then access that variable (and hence the form) through the window object.
File: URLs
In Navigator 3.0, when you use <SCRIPT SRC="..."> to load a JavaScript file, the URL specified in the SRC attribute could be any URL type (file:, http:, and so on), regardless of the URL type of the file that contained the SCRIPT tag. In Communicator 4.0, if you load a document with any URL other than a file: URL, and that document itself contains a <SCRIPT SRC="..."> tag, the internal SRC attribute cant refer to another file: URL. To get the 3.0 behavior in 4.0, users can add the following line to their preferences file:
user_pref("javascript.allow.file_src_from_non_file", true);
However, be cautious with this preference. It opens a security hole. Users shouldnt set this preference to true unless they have some overriding reason for accepting that risk.
Smith). A single HTML page can have scripts signed by different principals. The digital signature is placed in a Java Archive (JAR) file. If you sign an inline script, event handler, or JavaScript entity, Page Signer stores only the signature and the identifier for the script in the JAR file. If you sign a JavaScript file with Page Signer, it stores the source in the JAR file as well. The associated principal allows the user to confirm the validity of the certificate used to sign the script. It also allows the user to ensure that the script hasnt 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 validated 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. This document assumes that you are already familiar with the basic principles of object signing, using the Java Capabilities API, and creating digital signatures. The following documents provide information on these subjects:
1 Object Signing: Establishing Trust Downloaded for Softwareprovides overview an Netscape of object signing. Be sure you understand this material before using signed scripts.
Introduction to the Capabilities Classes2 gives more details on how to use the Java CapabilitiesAPI.Because signedscriptsusethis API torequest privileges,you need to understand this information as well. Java Capabilities API3 introduces the Java API used for object signing and provides details on where to find more information about this API. Using Page Signer4 describes the signing tool for creating signed JavaScript scripts. OverviewofObject-SigningResources5containsalistofdocumentsandresourcesthat provide information on object signing, from creating the Java applet to getting a certificate to packaging and signing it. JavaScriptGuide6containsinformationaboutusingLiveConnecttoaccessJavaclasses from a JavaScript script.
1. 2. 3. 4. 5. 6.
Note
Navigator 3.0 provided data tainting to provide a means of secure access to specific components on a page. Because signed scripts provide greater security than tainting, tainting has been disabled in Communicator 4.x.
Codebase Principals
As does Java, 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 Communicator. Fordeployment,yourscriptsshouldnotrelyoncodebaseprincipalsbeingenabled.You mightwanttoenablecodebaseprincipalswhendevelopingyourscripts,butyoushould sign them before delivery. To enable codebase principals, end users must add the appropriate preference to their Communicator preference file. To do so, add this line to the file:
user_pref("signed.applets.codebase_principal_support", true);
Even when codebase principals are disabled, Communicator keeps track of codebase principals to use in enforcement of the same origin security policy, described in Same Origin Policy on page 1. Unsigned scripts have an associated set of
1. http://developer.netscape.com/library/documentation/enterprise/mngserv/index.htm
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 displayedwithsignedscripts. The differenceis thatthis dialog asks the user to grant privileges based on the URL and doesnt provide author verification. It advisestheuserthatthescripthasnotbeendigitallysignedandmayhavebeentampered with.
Note
If a page includes signed scripts and codebase scripts, and signed.applets.codebase_principal_support is enabled, all of the scripts on that page are treated as though they are unsigned and codebase principals apply. Formoreinformationoncodebaseprincipals,seeIntroductiontotheCapabilitiesClasses.
For example, assume principals A and B have signed one script, but only principal A signed another script. In this case, a page with both scripts acts as if it were signed by only A. This assumption also means that if a signed script is on the same page as an unsigned script,bothscriptsactasiftheywereunsigned. Thisoccursbecausethesignedscripthas a codebase principal and a certificate principal, whereas the unsigned script has only a codebase principal. (See Codebase Principals on page 6.) The two codebase principals are always the same for scripts from the same page; therefore, the intersection of the principals of the two scripts yields only the codebase principal. This is also what happens if both scripts are unsigned. You can use the import and export functions to allow scripts signed by different principals to interact in a secure fashion. For information on how to do so, see Importing and Exporting Functions on page 18.
If all scripts on a page are signed by the same principals, container checks are applied to the window. If some scripts in a layer are signed by different principals, the special container checks apply to the layer. Figure 1 illustrates the method Communicator uses to determine which containers are associated with which sets of principals.
Figure 1 Assigning principals to layers.
Defined principals
Defined principals Assign scripts principals to window. Intersect scripts principals with those of layer containing JavaScript and assign result to that layer. If scripts principals are the same as the intermediate layers, do nothing. Otherwise assign scripts principals to layer containing JavaScript.
This method works as follows: Consider each script on the page in order of declaration, treating javascript: URLs as new unsigned scripts.
1.
If this is the first script that has been seen on the page, assign this scripts principals to be the principals for the window. (If the current script is unsigned, this makes the windows principal a codebase principal.) Done. If the innermost container (the container directly including the script) has defined principals,intersectthecurrentscriptsprincipalswiththecontainersprincipalsand assign the result to be the principals for the container. If the two sets of principals are notequal,intersectingthesetsreducesthenumberofprincipalsassociatedwiththe container. Done.
2.
3.
Otherwise,findtheinnermostcontainerthathasdefinedprincipals.(Thismaybethe window itself, if there are no intermediate layers.) If the principals of the current scriptarethe sameastheprincipalsofthatcontainer,leavethe principalsasis.Done. Otherwise,assignthecurrentscriptsprincipalstobetheprincipalsofthecontainer. Done.
4.
illustrates this process. For example, assume a page has two scripts (and no layers), with one script signed and theotherunsigned.Communicatorfirstseesthesignedscript,whichcausesthewindow object to be associated with two principalsthe certificate principal from the signer of thescriptandthecodebaseprincipalderivedfromthelocationofthepagecontainingthe script. When Communicator sees the second (unsigned) script, it compares the principals of that script with the principals of the current container. The unsigned script has only one principal,thecodebaseprincipal.Withoutlayerstheinnermostcontaineristhewindow itself, which already has principals. Becausethesetsofprincipalsdiffer,theyareintersected,yieldingasetwithonemember, the codebase principal. Communicator stores the result on the window object, narrowing its set of principals. Note that all functions that were defined in the signed scriptarenowconsideredunsigned.Consequently,mixingsignedandunsignedscripts on a page without layers results in all scripts being treated as if they were unsigned. Now assume the unsigned script is in a layer on the page. This results in different behavior. In this case, when Communicator sees the unsigned script, its principals are again compared to those of the signed script in the window and the principals are found tobedifferent.However,nowthattheinnermostcontainer(thelayer)hasnoassociated principals,theunsignedprincipalsareassociatedwiththeinnermostcontainer;theouter container (the window) is untouched.In this case, signed scripts continue to operate as signed.However,accessesbytheunsignedscriptinthelayertoobjectsoutsidethelayer are rejected because the layer has insufficient principals. See Isolating an Unsigned Layer within a Signed Container on page 17 for more information on this case.
ARCHIVE attribute
All signed scripts (inline script, event handler, JavaScript file, or JavaScript entity) require a SCRIPT tags ARCHIVE attribute whose value is the name of the JAR file containing the digital signature. For example, to sign a JavaScript file, you could use this tag:
<SCRIPT ARCHIVE="myArchive.jar" SRC="myJavaScript.js"> </SCRIPT>
Event handler scripts do not directly specify the ARCHIVE. Instead, the handler must be preceded by a script containing ARCHIVE. For example:
<SCRIPT ARCHIVE="myArchive.jar" ID="a"> ... </SCRIPT>
Unless you use more than one JAR file, you need only specify the file once. Include the ARCHIVE tag in the first script on the HTML page and the remaining scripts on the page use the same file. For example:
<SCRIPT ARCHIVE="myArchive.jar" ID="a"> document.write("This script is signed."); </SCRIPT> <SCRIPT ID="b"> document.write("This script is signed too."); </SCRIPT>
ID Attribute
Signed inline and event handler scripts require the ID attribute. The value of this attribute is a string that relates the script to its signature in the JAR file. The ID must be unique within a JAR file. When a tag contains more than one event handler script, you only need one ID. The entire tag is signed as one piece. In the following example, the first three scripts use the same JAR file. The third script accesses a JavaScript file so it doesnt use the ID tag. The fourth script uses a different JAR file, and its ID of "a" is unique to that file.
<HTML> <SCRIPT ARCHIVE="firstArchive.jar" ID="a"> document.write("This is a signed script."); </SCRIPT> <BODY onLoad="alert('A signed script using firstArchive.jar')" onLoad="alert('One ID needed for these event handler scripts')" ID="b"> <SCRIPT SRC="myJavaScript.js"> </SCRIPT> <LAYER> <SCRIPT ARCHIVE="secondArchive.jar" ID="a"> document.write("This script uses the secondArchive.jar file.");
When the script calls this function, the signature is verified, and if the signature is valid, expanded privileges can be granted. If necessary, a dialog displays information about the applications author, and gives the user the option to grant or deny expanded privileges. Privileges are granted only in the scope of the requesting function and only after the request has been granted in that 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: 5: 2: 3: 1: 4: 6: 8: disabled disabled disabled enabled enabled enabled disabled disabled
Function g requests expanded privileges, and only the commands and functions called after the request and within function g are granted privileges.
<SCRIPT ARCHIVE="ckHistory.jar" ID="a"> 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>
Targets
The types of information you can access are called targets. These are listed below.
Target
UniversalBrowserRead
Description Allowsreadingofprivilegeddatafromthebrowser.This allows the script to pass the same origin check for any document. Allowsmodificationofprivilegeddatainabrowser.This allows the script to pass the same origin check for any document. Allowsbothreadingandmodificationofprivilegeddata from the browser. This allows the script to pass the same origin check for any document.
UniversalBrowserWrite
UniversalBrowserAccess
Target
UniversalFileRead
Description Allows a script to read any files stored on hard disks or other storage media connected to your computer. Allows the script to read preferences using the navigator.preference method. Allows the script to set preferences using the navigator.preference method. Allows the program to send mail in the users name.
UniversalPreferencesRead
UniversalPreferencesWrite
UniversalSendMail
window object: Allow 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
enableExternalCapture
To capture events in pages loaded from different servers. Follow this method with captureEvents. To unconditionally close a browser window. To move a window offscreen. To move a window offscreen. 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 titlebar 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. To resize a window smaller than 100 x 100 pixels or larger than the screen can accommodate.
resizeBy
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. To set the inner height of a window to a size smaller than 100 x 100 or larger than the screen can accommodate.
innerHeight
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 ARCHIVE="myArchive.jar" ID="a"> 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">
1. http://developer.netscape.com/news/viewsource/index.html 2. http://developer.netscape.com/news/viewsource/goodman_sscripts.html
Remove comments that include international characters. There is no restriction on international characters the HTML surrounding the signed scripts.
objects or properties that arent functions. Importing a function into your scope creates a new function of the same name as the imported function. Calling that function calls the corresponding function from the secure container. To use import and export, you must explicitly set the LANGUAGE attribute of the SCRIPT tag to "JavaScript1.2". In the signed script that defines a function you want to let other scripts access, use the export statement. The syntax of this statement is:
exportStmt ::= export exprList exprList ::= expr | expr, exprList
where each expr must resolve to the name of a function. The export statement marks each function as importable. In the script in which you want to import that function, use the import statement. The syntax of this statement is:
importStmt ::= import importList importList ::= importElem | importElem, importList importElem ::= expr.funName | expr.*
Executingimportexpr.funNameevaluatesexprandthenimportsthefunNamefunctionof that object into the current scope. It is an error if expr does not evaluate to an object, if there isno function named funName,or ifthe function exists but has not beenmarkedas importable. Executing import expr.* imports all importable functions of expr.
Example
The following example has three pages in a frameset. The file containerAccess.html defines the frameset and calls a user function when the framesetisloaded.Onepage,secureContainer.html,hassignedscriptsandexports a function. The other page, access.html, imports the exported function and calls it. While this example exports a function that does not enable or require expanded privileges,youcanexportfunctionsthatdoenableprivileges.Ifyoudoso,youshouldbe very careful to not inadvertently allow access to an attacker. For more information, see Be Careful What You Export on page 21.
File containerAccess.html
<HTML> <FRAMESET NAME=myframes ROWS="50%,*" onLoad="inner.myOnLoad()"> <FRAME NAME=inner SRC="access.html"> <FRAME NAME=secureContainer SRC="secureContainer.html"> </FRAMESET> </HTML>
File secureContainer.html
<HTML> This page defines a variable and two functions. Only one function, publicFunction, is exported. <BR> <SCRIPT ARCHIVE="secureContainer.jar" LANGUAGE="JavaScript1.2" ID="a"> function privateFunction() { return 7; } var privateVariable = 23; function publicFunction() { return 34; } export publicFunction; netscape.security.PrivilegeManager.enablePrivilege( "UniversalBrowserRead"); document.write("This page is at " + history[0]); // Privileges revert automatically when the script terminates. </SCRIPT> </HTML>
File access.html
<HTML> This page attempts to access an exported function from a signed container. The access should succeed. <SCRIPT LANGUAGE="JavaScript1.2"> function myOnLoad() { var ctnr = top.frames.secureContainer; import ctnr.publicFunction; alert("value is " + publicFunction()); } </SCRIPT> </HTML>
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.
Now any other script can import myEval and read and write any file on the users hard disk using trust the user has granted to you.
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 ARCHIVE="historyWin.jar" ID="a"> 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 into Java to revert the privilege by introducing a function:
<SCRIPT ARCHIVE="historyWin.jar" ID="a"> 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>
Signing Scripts
win.close(); </SCRIPT>
Signing Scripts
During development of a script youll eventually sign, you can use codebase principals for testing, as described in Codebase Principals on page 6. Once youve finished modifying the script, you need to sign it. For any script to be granted expanded privileges, all scripts on the same HTML page or layer must be signed. If you use layers, you can have both signed and unsigned scripts as long as you keep them in separate layers. For more information, see Signed Script Policy on page 4. You can sign JavaScript files (accessed with the SRC attribute of the SCRIPT tag), inline scripts, event handler scripts, and JavaScript entities. You cannot sign javascript: URLs. Before you sign the script, be sure youve properly identified it, as described in Identifying Signed Scripts on page 10.
1. http://developer.netscape.com/library/documentation/signedobj/command/index.htm
After Signing
Once youve signed a script, any time you change it you must resign it. For JavaScript files, this means you cannot change anything in the file. For inline scripts, you cannot change anything between the initial <SCRIPT ...> and the closing </SCRIPT>. For event handlers and JavaScript entities, you cannot change anything at all in the tag that includes the handler or entity. A change can be as simple as adding or removing whitespace in the script. Changes to a signed scripts byte stream invalidate the scripts 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 doesnt 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. Although you cannot make changes to the script, you can make changes to the surrounding information in the HTML file. You can even copy a signed script from one file to another, as long as you make sure you change nothing within the script.
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.