ScriptLock
 
 

ScriptLock XSS


The code execution prevention system for JavaScript

Implementation

ScriptLock is a code execution prevention mechanism for JavaScript / Jscript / ECMAScript, which protects your pages against the effects of Reflected, Persistent and Self-XXS.
ScriptLock wraps security around the vulnerable parts of JavaScript that can permit hackers to transmit data from your website. It can warn you and your users when an unauthorised attempt is made to use these functions.
ScriptLock works on the latest versions of the leading browsers, including Internet Explorer 8, 9, Chrome 18, Safari 5.1+ and Firefox 6.0+.  Given current browser market share statistics, the benefit is that 78% of your users will be protected.

What is involved in implementing ScriptLock?

For the website developer, the task of implementing ScriptLock involves several steps:
  1. The majority of JavaScript code needs to be placed in external .js files. The ScriptLock server merges dynamically generated passwords/tokens into the .js files when they are requested. ScriptLock also uses natural division between the html page and external script files in order to protect the password. Any JavaScript code that directly uses protected functions must be placed in an external .js file.
  2. Calls to protected functions need to be amended to include a place-holder for the password. A few JavaScript functions are completely replaced with new ones. We are presently building a library of third-party JavaScript libraries that have been modified to work with ScriptLock.
  3. The developer must examine their code closely to make sure that they have not re-exposed protected functions through their own API. For instance, is there a wrapper function to createElement that exposes the same level of freedom to the caller? If so then the wrapper function will require the addition of a password parameter.
  4. The website will require proper session handling. ScriptLock requires a session identifier to generate a consistent and unique password for each session.
  5. Finally, the ScriptLock service needs to be installed on your webserver.

How it works

ScriptLock comprises of the scriptlock.min.js rewriter script and the ScriptLock script server.
When a webpage loads the rewriter script is downloaded first and the scriptlock_protect() function is invoked. The scriptlock_protect() function examines browser compatibility and then, where the relevant ECMAScript/JavaScript/JScript standards are supported, it rewrites at risk native functions and objects at the prototype level to require the presentation of a password.
The protection takes advantage of the natural division between the webpage and external script files to maintain the anonymity of the password. The toString() function is neutralised to stop malicious script from examining the scriptlock.min.js source code.
ScriptLock uses a convection mechanism to make sure that any child IFRAMES, FRAMES, windows or document fragments are also protected in order to eliminate any source of references to native functionality. Therefore once the main document is protected it is usually not necessary to call scriptlock_protect() again.
Any scripts that are used by the webpage must be served up by the ScriptLock service. Each script is modified so that calls to the rewritten-functions contain a place-holder for the password. The scripts are stored in a repository and when required they are served up by the ScriptLock service and the password merged in.
The place holder for the password takes the form "/*!param:hash*/". This makes it compatible with the yuicompressor minimiser.
Normally ScriptLock is configured to generate one password per session. However it is possible to generate a different password for each page refresh if very tight security is required.

Basic integration

Overview

To provide comprehensive protection the ScriptLock Service should be used to serve up all the JavaScripts on your website. Therefore it is usual to map anything under /js/* to the service.

Installation

Tomcat – war file installation

  1. Use the Tomcat Manager to deploy the Apache Tomcat/webapps /js.war file in the ScriptLock installation.
  2. Locate the js/WEB_INF/CCSettings.xml file and make the following amendments:

    Set the privatePath value to a local path on your server where you will store your JavaScript files. Note, this should not be accessible to your web service.

    Set the privateKey to a random value with at least 20 characters.
  3. Use the Tomcat Manager to restart the “js” servlet.

Tomcat – manual installation

  1. Copy the “js” directory from the “Apache Tomcat/webapps” directory in the installation to the Tomcat “webapps” directory on your server. 
  2. Locate the js/WEB_INF/CCSettings.xml file and make the following amendments:

    Set the privatePath value to a local path on your server where you will store your JavaScript files. Note, this should not be accessible to your web service.

    Set the privateKey to a random value with at least 20 characters.
  3. On Tomcat 5 and earlier you will need to create a js.xml file in Tomcat/conf/Catalina/localhost for the new servlet to be picked up.
  4. Restart Tomcat.

Isapi_redirect.dll

If you are using the IIS isapi_redirect.dll, then you should add a mapping in the uriworkermap.properties file:
/js/*=wlb

Move and modify your JavaScript

Any JavaScript that uses rewritten functions should be placed in external .js files and these files place in the ScriptLock repository, which is the “privatePath” set up above.
Perform a search and replace on protected functions to replace calls to them with the correct ScriptLock call, as outlined in the rewritten functions section below. Note, that any wrapper functions (that is functions that surface all or part of the functionality of a protected function) will also need to take a password as a parameter.
Any libraries used by your website will also need modifying. We are building a library of converted third party libraries which are listed in the ScriptLock ready libraries section below.

The rewritten functions

The following functions are presently rewritten by ScriptLock:
Original call ScriptLock call
Function.toString() Function.toString(), returns no result
document.open() window.safeOpen(document,"/*!param:hash*/")
document.close() window.safeClose(document,"/*!param:hash*/")
document.write(params) window.safeWrite(document,"/*!param:hash*/",params)
document.writeln(params) window.safeWriteln(document,"/*!param:hash*/",params)
document.createElement(value) document.createElement(value,"/*!param:hash*/")
document.importNode(node,deep) document.importNode(node,deep,"/*!param:hash*/")
document.cookie windows.getCookie(document,"/*!param:hash*/")
document.cookie = value windows.setCookie(document,value,"/*!param:hash*/")
implementation.createHTMLDocument(title) implementation.createHTMLDocument(title,"/*!param:hash*/")
new win.XMLHttpRequest() new win.XMLHttpRequest("/*!param:hash*/")
new win.ActiveXObject(name) new win.ActiveXObject(name,"/*!param:hash*/")
win.open(url,name,features) win.open(url,name,features,"/*!param:hash*/")td>
element.src = value window.setSrc(element,value,"/*!param:hash*/")
element.href = value window.setHref(element,value,"/*!param:hash*/")
element.action = value window.setAction(element,value,"/*!param:hash*/")
element.innerHTML = value window.setInnerHTML(element,value,"/*!param:hash*/")
element.outerHTML = value window.setOuterHTML(element,value,"/*!param:hash*/")
element.appendChild(node) element.appendChild(node,"/*!param:hash*/")
element.cloneNode(node) element.cloneNode(node,"/*!param:hash*/")
element.insertAjacentHTML(where,value) element.insertAjacentHTML(where,value,"/*!param:hash*/")
element.insertAjacentElement(where,node) element.insertAjacentElement(where,node,"/*!param:hash*/")
element.insertBefore(new_node,existing_node) element.insertBefore(new_node,existing_node,"/*!param:hash*/")
element.removeChild(node) element.removeChild(node,"/*!param:hash*/")
element.removeNode(remove_children) element.removeNode(remove_children,"/*!param:hash*/")
element.replaceChild(new_node,old_node) element.replaceChild(new_node,old_node,"/*!param:hash*/")
element.setAttribute(attrib_name,attrib_value,case_sensitivity) element.setAttribute(attrib_name,attrib_value,case_sensitivity,"/*!param:hash*/")
element.setAttributeNode(node_reference) element.setAttributeNode(node_reference,"/*!param:hash*/")
element.setAttributeNodeNS(node_reference) element.setAttributeNodeNS(node_reference,"/*!param:hash*/")
element.setAttributeNS(attrib_ns,attrib_qn,attrib_value) element.setAttributeNS(attrib_ns,attrib_qn,attrib_value,"/*!param:hash*/")
element.removeAttribute(attrib_name,case_sensitivity) element.removeAttribute(attrib_name,case_sensitivity,"/*!param:hash*/")
element.removeAttributeNode(node_reference) element.removeAttributeNode(node_reference,"/*!param:hash*/")
element.removeAttributeNS(attrib_ns,attrib_ln) element.removeAttributeNode(attrib_ns,attrib_ln,"/*!param:hash*/")

ScriptLock ready libraries

The following libraries have been converted to ScriptLock and are available for download:
Library Version
Prototype.js 1.4.0
tinymce.js 3.9.3
bsn.AutoSuggest_c_2.0 2.0
Baron Schwartz’s InputMask 1.3
swfobject.js 2.2

Modify your HTML

Your HTML pages will require the addition of the ScriptLock protection in the <head> section of the html, as follows:
     <head>
       <script
           src="js/scriptlock.min.js?sid={$sessionID}&amp;pid={$pageID}"
           type="text/javascript">;
       </script>
       <script>
           scriptlock_status = scriptlock_protect();
       </script>
The protection should always go at the top of the <head> section to make sure it is called before any other scripts. This minimises the risk of a Persistent-XSS attack succeeding.

{$sessionID} should be filled by your server-side CGI with the current session identifier.
{$pageID} is an optional parameter used to identify the page or a page refresh. This allows a different password to be generated on each page load. However, be warned that there will be no opportunity for caching if you do this, which may cause slower refreshing of your websites pages.

Provide user feedback

Lastly, you may wish to tell your users that they are protected by ScriptLock.
<a href="{$baseURL}scriptlock"><img style="float:left;margin-right:8px;border:none;" src="js/scriptlock_32.png"/><div style="float:left;clear:right;vertical-align:middle;font-size:24px;" id="scriptlock_status">ScriptLock</div></a>
<script>
var e = document.getElementById("scriptlock_status");
if (e) e.style.color = scriptlock_status.isDeadlocked?"green":(scriptlock_status.isProtected?"orange":"red");
</script>

Advanced Integration Topics

Customising the ScriptLock Service

Java based servlet

The java based servlet provides the getHash function for you to integrate the password generation with your session handling code.
protected String getHash(String sessionID, String pageID)
The standard version of getHash() simply creates a Hex encoded SHA-1 hash of the sessionID, pageID and the privateKey. Ideally the privateKey should not be a static value, but one that differs for each session: a session secret. Therefore we recommend your global session handling code stores a random secret for each session and that that the getHash() function is augmented to retrieve the session secret and generates a hash using the sessionID, pageID and the session secret.

Script adaptation gotcha’s

Adapting scripts to ScriptLock is normally a search and replace task. However, there are some gotcha’s to be aware of especially where the scripts being converted create some sort of API or wrapper around the protected functions.

Window object functions

ScriptLock creates a number of functions on the window object, including safeOpen, safeWrite, safeWriteln, safeSetSrc
It is important that when these functions are used from the correct window object that owns or contains the document or element that they are acting on.

XML Documents

Protection is not needed for XML Documents, as they reside in a separate security scope to the parent window and cannot therefore be exploited to reverse or circumvent the ScriptLock protection.  However, a quirk in Internet Explorer means that you must treat them as being protected.
In Internet Explorer XML documents created with createDocument inherit the prototype function protection of the parent window automatically. The same does not happen in Firefox. XML documents created with ActiveXObject('MSXML2.DOMDocument',"/*!param:hash*/") or ActiveXObject('Microsoft.XmlDom',"/*!param:hash*/") do not inherit any prototype function protection either.
This quirk does not normally cause problems because you can pass the password parameter to functions in the unprotected XML document as though they were protected and the parameter is just ignored.
function getXML() {
    var i = document.implementation;
        if (!i || !i.createDocument) {
            // Try IE objects
            try {return new ActiveXObject('MSXML2.DOMDocument',"/*!param:hash*/");} catch (ex) {}
            try {return new ActiveXObject('Microsoft.XmlDom',"/*!param:hash*/");} catch (ex) {}
        } else
            return i.createDocument('', '', null);
    };
var d = getXML();
var e = d.createElement("html","/*!param:hash*/");
In the above example if getXML returns a document created with createDocument on Internet Explorer, then the password parameter is required for createElement to work. Otherwise if it is create with ActiveXObject or created with createDocument on Firefox, then the password parameter used in createElement is superfluous and ignored.

HTML Documents

HTMLDocuments created with createHTMLDocument are protected automatically. Any calls to protected functions within the scope of these documents must be made with the password parameter.

IFRAMES/FRAMES

Same domain Iframes and frames are automatically protected by ScriptLock at the point of creation: as they are created by setInnerHTML, setOuterHTML or as they are attached to a document using appendChild, etc. If the IFRAME/FRAME loads an html document the protection is deleted, so it is up to the loaded html document to reapply the ScriptLock protection in its <head> element.

Child Windows

Unlike IFRAMES, child windows are not automatically protected by window.open(). This behaviour may change in future versions of ScriptLock. As with IFRAMES it is up to the loaded html document to apply the protection in its <head> element.

XMLHttpRequest

The XMLHttpRequest constructor is protected by a password parameter, however the methods/functionality of the XMLHttpRequest object itself are not protected.

If a hacker can get hold of an XMLHttpRequest object they can perform a same domain request to the scriptlock.min.js file and obtain details of the password.

It is therefore very important that you keep any XMLHttpRequest objects away from global variables. Always assign them to local variables defined with “var” and keep their persistence to a minimum.

var x = new XMLHttpRequest("/*!param:hash*/")

NOT

x = new XMLHttpRequest("/*!param:hash*/")