Defensive JavaScript

Introduction DJS is a defensive subset of JavaScript: code in this subset runs independently of the rest of the JavaScript environment. When propertly wrapped, DJS code can run safely on untrusted pages and keep secrets such as decryption keys. DJS is especially useful to write security APIs that may run alongside other untrusted scripts, for instance an OAuth library such as the one used by "Login with Facebook", or a Node.js program that includes untrusted modules. Techical details can be found in our USENIX Security 2013 paper Language-Based Defenses Against Untrusted Browser Origins. Our security properties do rely on assumptions on JavaScript implementations that have been violated in the past due to bugs and may not hold in ECMA Script 6. We also provide details about attacks and their disclosure on security components that may be loaded in untrusted pages. In addition, the fragment of DJS without loops and arrays, but extended with our cryptographic library DJCL, can be translated into ProVerif spi-calculus processes, in particular, it can generate the client processes for the WebSpi library automatically to verify the DJS implementations of complex web protocols. Finally, because DJS is proved to run independently of the JavaScript environment, it provides a more expressive subset for compilation to native code than the existing best effort, asm.js.

Current work: we now compile statically typed JavaScript (similar to DJS) to asm.js. Contact information If you have questions, comments or find a bug or attack, please contact Antoine Delignat-Lavaud, Karthikeyan Bhargavan or Sergio Maffeis. Frequently asked questions Following our USENIX Security presentation, we have received questions that may be of public interest. In the paper, you claim you can embed DJCL in a 2KB bookmarklet. How is that possible? A: We claim we can trim down DJCL to fit in a bookmarklet. There are various ways to achieve this, the most space efficient being to use hexadecimal strings internally. For instance, this sample bookmarklet can do HMAC-SHA1 with a secret key (assuming the input is an hexadecimal string), and compresses to 1868 bytes. Encryption is also possible with some effort.

Isn't it possible to redefine Function.prototype.call, Object.constructor, Array.constructor and tamper with literal function calls, objects and arrays? A: If you redefine Function.prototype.call in the Chrome or Firefox console, it may seem like function calls are tampered - however, this is only due to the way the console tool is implemented - it itself is written as a JavaScript wrapper. As for the object and array constructors, the ECMA specification clearly states that the built in constructor should always be used for literal objects and arrays, however, some browsers didn't implement this behavior until 2008 when cross-origin JSON hijacking attacks have been mounted by tampering those constructors.

Is it possible for two different scripts on the same untrusted origin to authenticate and communicate securely? A: Yes, you can even implement a TLS channel between two scripts provided they embed a shared secret or user-specific public keys for authentication. As always when dealing with JavaScript cryptography, key management is the biggest issue - your script server must act as a trust anchor and long term secret manager. Note that in general, it is easier and more efficient to use postMessage between frames from trusted origins instead.

How do you defend against side channels? A: We only focus on semantic language safety, and if you use the translation to ProVerif, the correctness of the protocol implementation. There are many side channels available to a same-origin attacker - the most obvious ones being time and memory. In SJCL, we sometimes try to mitigate those channels - for instance, in CCM tag validation. However, we do not have any formal proof of robustness against any side-channel attacker - furthermore, user-written DJS code must also take into account this risk. For instance, use the AES.ctEq function for constant time string comparison.

Should I use DJS for my application? A: If you are not sure whether you would benefit from using DJS for your application, you should probably not try to write DJS code. There are few sensitive applications that need to defend against same origin attackers, and good security knowledge is required to write (and verify) them properly. There are exceptions - for instance, you may want to write a CSRF protection system that is safe against an XSS attacker. For those frequent use case, we will ultimately provide our own verified, easy to use solutions. Following our USENIX Security presentation, we have received questions that may be of public interest.

DJS Crypto Library DJCL is a complete cryptographic library written in DJS, based on SJCL and JSBN, two popular JavaScript libraries for symmetric and asymmetric encryption. It supports AES-256 in CBC and CCM mode, HMAC on top of SHA1 or SHA-256, RSA encryption and signature with OAEP/PSS or PKCS#1 v1.5 padding.



You can try to tamper base object prototypes and default functions on the demo page and verify the independance of the cryptograpic compoutations.

N.B. We do not guarantee robustness against side-channel attacks at this time. Current version of DJCL - Documentation Note that the various DJCL components should be loaded together inside a DJS wrapper to be secure against a same origin attacker. See the Script Server tab for a demo of how to embed DJCL and encryption keys into a security component. Encoding and transcoding Input encoding: Output encoding: Hashing Algorithm: SHA-256 SHA1 Input encoding: Output encoding:

HMAC AES encryption Key:

IV: CBC (PKCS#5 padding)

CCM Tag length: 4 6 8 10 12 14 16

Direction: encrypt decrypt Input encoding: Output encoding: RSA encryption and signature PKCS#1 Private key (generate one with openssl genrsa) Tag length:Direction:Input encoding:Output encoding:(generate one with openssl genrsa) -----BEGIN RSA PRIVATE KEY----- MIICXAIBAAKBgQDSbCFDl5kOdzPU0WY5hsIgnOE788JjktUu/qqDbtebr6J2Tj5s 4QZsgFMu/QXIdkK/qEFJ3kkdJ3MPrEH8ZPkqq2bOLVAC+pWZ9oMWy3ke1NNbtqPb xGMXIfPD8rfNn4a8vXhoiJuyLn2hSENw14qBCLn7o8NaNvXehP7IWi/QHwIDAQAB AoGAO6O4Hd9G20m/4A7lqWJffTzZvdNOAkjosWQu0gfFhnPWWS++E8AHwRLyALxo OjQCoS3AK36cPG4k94k8Ppwj3odMbRMyTMG70ZB1+mXK2OXShdIyFZ3Oa/9/kBmh CKZT/6bV3Zr8/Ml+KqrCjm8QCntNLPYkHAo2VB+Er4QhTeECQQD4g8oNUWNbvIrK bfT1x9GUPUxe53JYt1D1SJiwL7ApiR6LnoLHw5ApPJUZJxaHngDV8e+WnzY2luLt jbwcOnaPAkEA2MKfk7PtCIiqOq8XqrQI7QUz3momW+rs/9eJ5eMyPTa+laqzsv++ z7KxBHBPUcchX6yMZf4WDLbvRck89rxVcQJACJiudZV6JWM5PdVd4t6dnk4chS/m YbE9qK5xMa8EnfszRkseZQCbzQFeevdCLUDG8J+k1QX+3xaLFQKRHjzbewJAHC3x IPqbLijWOJrasM6G+olanOef5QM9nGUhAEnxFhQv4rU2d2bYH5hTewg+x5rjs9Ry zC+kHjMKGEB5NHp3gQJBAIWCIuTG7lgQjA3DiY+lOWgBH4CQbt1z0sKaQBpTl45C E2g/H3V4oJhpxlosAfqlIV8GKfyar3MLXSD+0/YsdW4= -----END RSA PRIVATE KEY-----

Key details Modulus Public exponent Private exponent First prime p Second prime q d mod p d mod q q^-1 mod p Encryption (OAEP padding) Hash function SHA1 SHA-256

encrypt decrypt Input encoding Output encoding

Signature Input encoding Output encoding Hash function SHA1 SHA-256

PKCS#1v1.5 padding

PSS padding

JWT-RS256 (JSON Web Token) Action: sign check signature Hash functionInput encodingOutput encodingInput encodingOutput encodingHash function

Defensive Script Server How it works Defensive scripts must be loaded from a secure isolated origin to preserve source secrecy and other security properties. The primary reason to use defensive scripts is to keep secrets, such as encryption keys, even when loaded into an attacker's page. The purpose of a defensive script server is to embed keys and entropy into the script and making sure that the origin it is being loaded into is authenticated. We implement a demonstration script server both to serve our examples and be used as a trusted third party script server, where one can register scripts for his own origin with a public key. The source code of our defensive script server is available at https://dss.defensivejs.com. Example use We have registered www.defensivejs.com on the defensive script server at dss.defensivejs.com to serve a script that retrieves the time in Los Angeles from the host page's server. This script must authenticate the origin it is being loaded into; there are several ways of doing this, one of the safest is to use the CORS-based encrypted AJAX method described in the USENIX Security paper. The script is loaded using: <script src="https://dss.defensivejs.com/?script=1"></script>. The security goal for this script is that the server time will only be released on www.defensivejs.com - trying to load the same script on any other origin must fail, and no other script may be able to tamper the displayed time assuming the alert() function is not tampered (however, other scripts may cause the AJAX call to fail). It is recommended to use your web console (F12) to track the HTTP requests and scripts loaded from the example. Registered JavaScript code /** Defensive part (body of defensive() function) **/ var so = {iv:"", e:"", hs:"", hm:""}; if(input == "") return rsa.encrypt(encoding.hstr2astr(hostKey), publicKey); hashing.hmac_hash = hashing.sha256; if(!DJSON.parse(input, so, {type:'object', props:[ {name: "iv", value: {type: "string", props:[]}}, {name: "e", value: {type: "string", props:[]}}, {name: "hs", value: {type: "string", props:[]}}, {name: "hm", value: {type: "string", props:[]}} ]}) || hashing.HMAC(hashing.HMAC(encoding.hstr2astr(so.hs), encoding.hstr2astr(hostKey)), so.e) != so.hm) return "FAILED"; aes.setKey(encoding.hstr2astr(hostKey)); return aes.CBC(encoding.base64_decode(so.e), encoding.base64_decode(so.iv), true); /** Outer code (functionality can be distrupted) **/ var e = defensive(''); _xhr.open('GET', 'dss.php?getTime='+e, 1); _xhr.onload = function(){alert(defensive(_xhr.responseText+''))}; _xhr.send(); Server-side code <?php if(isset($_GET['getTime'])) { require_once "Crypt/RSA.php"; require_once "Crypt/AES.php"; $rsa = new Crypt_RSA(); $rsa->setHash("sha256"); $rsa->setMGFHash("sha256"); $t = pack("H*", $_GET['getTime']); $rsa->loadKey("-----BEGIN RSA PRIVATE KEY-----"); // ... $r = $rsa->decrypt($t); if(strlen($r) != 32) die; $cipher = new Crypt_AES(); $cipher->setKey($r); $iv = openssl_random_pseudo_bytes(16); $hs = openssl_random_pseudo_bytes(16); $cipher->setIV($iv); date_default_timezone_set('America/Los_Angeles'); $text = "The time in Los Angeles is ".date('l jS \of F Y h:i:s A T'); $e = base64_encode($cipher->encrypt($text)); die(json_encode(array("iv"=>base64_encode($iv),"e"=>$e, "hs"=>bin2hex($hs), "hm"=>hash_hmac("sha256",$e, hash_hmac("sha256", $r, $hs))))); } Use it on your own website Step 1: origin registration You must first register your website. First, generate the private key offline using openssl genrsa -out key.pem 2048. Export the public key using openssl rsa -in key.pem -pubout -out pub.pem. Finally, put the public key on the webserver where you wish to load your defensive script. Enter the URL of the public key below to complete registration of your origin, note that it must include the protocol, domain and port as they would appear in the Origin header. Note that if the origin was already registered it will update the public key. Step 2: Script registration Origin: If you want to update a script enter its ID:

This script requires the UTF-8 capable DJCL (needs an extra 200KB to load).

// Defensive script to register // Variables in scope: input (of defensive script), // DJCL objects (encoding, rsa, aes), origin, hostKey, sessionKey, publicKey

// Outer (non defensive) script // Variables in scope: _xhr (XMLHttpRequest instance), defensive

