Scriptlock 2.1


The code execution prevention system for JavaScript

Demonstration

The best way to prove the effectiveness of Scriptlock is to show it in action.

Scriptlock provides fully deadlocked protection in IE 10, IE11, Edge 12+, Safari 6 +, Chrome 18.0+, Firefox 6.0 +, Opera 10.5+. So if you are using or have access to any of these browsers then you will be able to see the different protection mechanisms provided by Scriptlock at work.

There are broadly three protection mechanism that Scriptlock applies depending on the browser you are using:

  1. Pre-CSP protection
  2. CSP1.0 protection
  3. CSP2.0 protection

The following table shows which browser versions you will need to be using for each protection mechanisms. There are still reference copies of all versions of Firefox readily available from the Mozilla website. IE11 is still shipped with Windows 10 and accessible from the run prompt iexplore.exe.

MechanismBrowser MakeVersion
pre-CSP Internet Explorer 10 - 11
pre-CSP Firefox* 6 - 22
pre-CSP Safari* 6
pre-CSP Chrome* 18-24
pre-CSP Opera 10.5-14
CSP 1.0 Edge 12 - 14
CSP 1.0 Firefox 23 - 30
CSP 1.0 Safari 7 - 9.1
CSP 1.0 Chrome 25 - 35
CSP 1.0 Opera 15-22


* these versions do support CSP1.0 but use non-standard headers.

Persistent-XSS protection

The following series of tests that demonstrate Scriptlock's abilities to protect agains Persistent XSS and Reflected XSS and variable hijacking. They also demonstrate a number of other features:

  • The 'inline-event' source, along with the event-nonce and the comment based nonce.
  • Script synchronisation in the CSP1.0 mechanism.
  • The effectiveness of Scriptlock's call-stack based algrotihm in the pre-CSP protection mechanism.

Note an event listener is added to each button to translate a "no action" outcome to a pass or fail as appropriate. You will therefore notice subtle differences in the results of tests between the pre-CSP protection mechanism, the CSP 1.0 mechanism and CSP 2.0. For instance Test 6 will return "No action (Pass)" with the first and "Pass" with the other two. This is entirely correct behaviour.

For each test bring up the F12 debugger to see the protection mechanisms kicking in.

Test Details Button Result
Test 1

Inline event on an element with no nonce specifed

<button id="button_for_test1" 
onclick="var el = document.getElementById('demonstration_test1'); el.innerHTML = '&lt;span style=&quot;color:red&quot;&gt;Fail&lt;span&gt;';">Test</button>
 
Test 2

Inline event on an element with a valid event-nonce attribute

<button id="button_for_test2" event-nonce="{$nonce}" 
onclick="var el = document.getElementById('demonstration_test2'); el.innerHTML = '&lt;span style=&quot;color:green&quot;&gt;Pass&lt;span&gt;';">Test</button>
 
Test 3

Inline event on an element with a valid comment based nonce

<button id="button_for_test3" 
onclick="/*!nonce:{$nonce}*/var el = document.getElementById('demonstration_test3'); el.innerHTML = '&lt;span style=&quot;color:green&quot;&gt;Pass&lt;span&gt;';">Test</button>
 
Test 4

Applying an inline function with an addEventListener call from a timeout invoked from within the scope of an inline script without a valid nonce.

<button id="button_for_test4">Test</button>

<script>
    var event_for_test_4 = function(e){var el = document.getElementById('demonstration_test4'); el.innerHTML = '<span style="color:red">Fail<span>';};
    setTimeout("var e = document.getElementById('button_for_test4'); e.addEventListener('click',event_for_test_4,false);",1000);
</script>
 
Test 5

Variable hijacking. addEventListener called from within the scope of an allowed inline script, inadvertantly applying a bad inline function.

<button id="button_for_test5">Test</button>

<script nonce="{$nonce}">
    scriptlock.permit();
    var event_for_test_5 = function(e){var el = document.getElementById('demonstration_test5'); el.innerHTML = '<span style="color:green">Pass<span>';};
</script>

<script>
    var event_for_test_5 = function(e){var el = document.getElementById('demonstration_test5'); el.innerHTML = '<span style="color:red">Fail<span>';};
</script>

<script nonce="{$nonce}">
    scriptlock.permit();
    var e = document.getElementById('button_for_test5');
    e.addEventListener('click',event_for_test_5,false);
</script>
 
Test 6

Variable hijacking. addEventListener called the scope of an allowed inline script, inadvertantly applying a bad inline function via a timeout.

<button id="button_for_test6">Test</button>

<script nonce="{$nonce}">
    scriptlock.permit();
    var event_for_test_6 = function(e){var el = document.getElementById('demonstration_test6'); el.innerHTML = '<span style="color:green">Pass<span>';};
</script>

<script>
    var event_for_test_6 = function(e){var el = document.getElementById('demonstration_test6'); el.innerHTML = '<span style="color:red">Fail<span>';};
</script>

<script nonce="{$nonce}">
    scriptlock.permit();
    setTimeout("var e = document.getElementById('button_for_test6'); e.addEventListener('click',event_for_test_6,false);",1000);
</script>
 
Test 7

Variable hijacking. addEventListener called in the scope of an allowed inline script, inadvertantly applying a bad inline function defined in an external script.

<button id="button_for_test7">Test</button>

<script nonce="{$nonce}">
    scriptlock.permit();
    var event_for_test_7 = function(e){var el = document.getElementById('demonstration_test7'); el.innerHTML = '<span style="color:green">Pass<span>';};
</script>

<script src="/Scriptlock/js/scriptlock.synchronize.js?iid={$scriptlockInstance}&pid={$pageID}" type="text/javascript">;</script>  
<script src="https://www.datawing.com/js/scriptlock/bad_script_test_7.js" type="text/javascript">;</script>

<script nonce="{$nonce}">
    scriptlock.permit();
    var e = document.getElementById('button_for_test7');
    e.addEventListener('click',event_for_test_7,false);
</script>
 
Test 8

addEventListener called in the scope of an allowed inline script, applying an anonymous function.

<button id="button_for_test8">Test</button>

<script nonce="{$nonce}">
    scriptlock.permit();
    var e = document.getElementById('button_for_test8');
    e.addEventListener('click',function(e){var el = document.getElementById('demonstration_test8'); el.innerHTML = '<span style="color:green">Pass<span>';},false);
</script>
 
Test 9

addEventListener called in the scope of an allowed inline script, applying an inline function defined in an allowed script.

<button id="button_for_test9">Test</button>

<script nonce="{$nonce}">
    scriptlock.permit();
    var event_for_test_9 = function(e){var el = document.getElementById('demonstration_test9'); el.innerHTML = '<span style="color:green">Pass<span>';};
</script>

<script nonce="{$nonce}">
    scriptlock.permit();
    var e = document.getElementById('button_for_test9');
    e.addEventListener('click',event_for_test_9,false);
</script>
 
Test 10

addEventListener called in the scope of a timeout spawned from an allowed inline script, applying an inline function defined in an allowed script.

<button id="button_for_test10">Test</button>  
            
<script nonce="{$nonce}">
    scriptlock.permit();
    var event_for_test_10 = function(e){var el = document.getElementById('demonstration_test10'); el.innerHTML = '<span style="color:green">Pass<span>';};
    setTimeout("var e = document.getElementById('button_for_test10'); e.addEventListener('click',event_for_test_10,false);",1000);
</script>
 
Test 11

Variable hijacking. Inadvertantly setting onclick property to a bad inline function in the scope of an allowed inline script

<button id="button_for_test11">Test</button>

<script nonce="{$nonce}">
    scriptlock.permit();
    var event_for_test_11 = function(e){var el = document.getElementById('demonstration_test11'); el.innerHTML = '<span style="color:green">Pass<span>';};
</script>

<script>
    var event_for_test_11 = function(e){var el = document.getElementById('demonstration_test11'); el.innerHTML = '<span style="color:red">Fail<span>';};
</script>
            
<script nonce="{$nonce}">
    scriptlock.permit();
    var e = document.getElementById('button_for_test11');
    e.onclick = event_for_test_11;  
</script>
 
Test 12

Variable hijacking. Inadvertantly setting onclick property to a function defined in a bad external script in the scope of an allowed inline script.

<button id="button_for_test12">Test</button>  
<script nonce="{$nonce}">
    scriptlock.permit();
    var event_for_test_12 = function(e){var el = document.getElementById('demonstration_test12'); el.innerHTML = '<span style="color:green">Pass<span>';};
</script>

<script src="/Scriptlock/js/scriptlock.synchronize.js?iid={$scriptlockInstance}&pid={$pageID}" type="text/javascript">;</script>          
<script src="https://www.datawing.com/js/scriptlock/bad_script_test_12.js" type="text/javascript">;</script>
   
<script nonce="{$nonce}">
    scriptlock.permit();
    var e = document.getElementById('button_for_test12');
    e.onclick = event_for_test_12;  
</script> 
 
Test 13

Setting onclick property to an anonymous function in the scope of an allowed inline script.

<button id="button_for_test13">Test</button>  
            
<script nonce="{$nonce}">
    scriptlock.permit();
    var e = document.getElementById('button_for_test13');
    e.onclick = function(e){var el = document.getElementById('demonstration_test13'); el.innerHTML = '<span style="color:green">Pass<span>';}; 
</script>
 
Test 14

Setting onclick property to an inline function, defined in an allowed script, from within the scope of an allowed inline script.

<button id="button_for_test14">Test</button>  
            
<script nonce="{$nonce}">
    scriptlock.permit();
    var event_for_test_14 = function(e){var el = document.getElementById('demonstration_test14'); el.innerHTML = '<span style="color:green">Pass<span>';};
    var e = document.getElementById('button_for_test14');
    e.onclick = event_for_test_14; 
</script>
 
Test 15

Asynchronous side-loading of script with an onloaded event (In this case the external script sets parameter_for_test_15 = 15).

<script nonce="{$nonce}">
    scriptlock.permit();
    var load_event_for_test_15 = function(e){var el = document.getElementById('demonstration_test15'); 
    if(parameter_for_test_15 && parameter_for_test_15 === 15) el.innerHTML = '<span style="color:green">Pass<span>';
    else el.innerHTML = '<span style="color:red">Fail<span>';};
    var click_event_for_test_15 = function(e){
        var el = document.getElementById('demonstration_test15');
        var s = document.createElement('SCRIPT');
        s.src ="js/scriptlock/good_script.js"
        s.onload = load_event_for_test_15; 
        el.appendChild(s);
    }
    var e = document.getElementById('button_for_test15');
    e.onclick = click_event_for_test_15; 
</script>