SBN

An Oxymoron : Static Analysis of a Dynamic Language (Part 1)

An Oxymoron : Static Analysis of a Dynamic Language (Part 1)

What are the characteristics of a Dynamic Language (JavaScript)?

Benjamin Pierce classifies programming languages along two axes:

whether they are safe or unsafe and whether they are statically or dynamically checked

A safe language is a language which protects its abstractions. For instance, that the run-time cannot be corrupted by writing past the end of some memory region, that uninitialized memory is not read, that lexically scoped variables cannot be changed outside their scope, and so on. A statically checked language is a language which ensures the safety of its abstractions at compile-time. For instance, by checking that all variables are initialized before they are used. A safe language, which is dynamically checked, must enforce these safety properties by performing checks at run-time.

JavaScript is interpreted and has no notion of compile-time. Thus, the run-time must check every operation to ensure its safety. It is thus recommended to apply static analysis during the development of, or before deploying a JavaScript application. Due to its dynamic and untyped nature, static analysis for the language is a challenging task. Analysis of JavaScript applications is more challenging than analysis of programs in other programming language such as C or Java because JavaScript does not have a static type system.

JavaScript is the most widely-used programming language for the client-side of web applications, powering over 100% of today’s web sites . It has also become increasingly popular for platforms beyond the browser: server-side (Node JS based); it is even used for writing operating systems (Firefox OS).

It also happens to be a dynamic, weakly typed language with first-class functions. It is weakly typed in the sense that is has objects, but no classes. It relies on prototyping instead of inheritance to share and extend functionality. The prototyping functionality of JavaScript makes it an expressive scripting language, but also exposes an environment in which runtime errors can occur.

Let us illustrate these concerns by firstly understanding the issues associated with weak typing:

#1 : JavaScript is a classless, object-based language with first-class functions

> var person = { name: “Adam”, toString: function() { return this.name }} 
> person.toString()
Adam
> person.wife = “Alice” 
Alice
> person.setWife = function(b) { this.wife = b} 
function (b) { this.wife = b; }
> person.setWife(“Eve”) 
> person.wife
Eve

A script demonstrating the classless and dynamic properties of JavaScript is illustrated above.

  • The script creates a person object with a field specifying the person’s name and a toString function that returns the name.
  • After instantiating the object and printing the name, a new field is added to the person specifying the person’s wife.
  • Next, a mutator for the wife field is added to the object. The mutator is then invoked, setting the wife field to a new value.

The script demonstrates two features that make static type checking in JavaScript difficult: user-defined objects can be created without the definition of a class, and member variables and functions can be dynamically added to objects.

#2 : Functions in JavaScript do not require type annotations for input parameters or return types

> function add(a, b) { return a + b } // what is the type of 'a' and 'b'
> function add() { return arguments[0] + arguments[1] } // what is the return type
> add(1, 0) // pass Number type
1
> add(“Hello “, “World”) // pass String type
Hello World
> add() // pass no arguments 
NaN
> add(1,2,0) // pass more (3) that supported (2) arguments 
3
  • JavaScript is a loosely typed programming language. Variables are defined using the var keyword, which places no restrictions on the type of a variable.
  • A variable can be defined as an integer and later used as a string or boolean type. The following types are provided in JavaScript: number, boolean, string, object and function. The type of a variable can be determined using the typeof keyword, as demonstrated above.
  • JavaScript uses the inferred type of variables to determine the operation to perform. For example, it is necessary to determine whether the “+” symbol is intended for addition of numbers or concatenation of strings.

#3 JavaScript allows a variable to modify its type based on the context of its usage

> typeof 0  
number
> typeof true  
boolean
> typeof "string"  
string
> typeof (new Array())  
object
> typeof function f() {}  
function
> var x = 1 
> x = !!x
true
> x += "string"  
truestring
> x = 10 + !!x  
11
> x = "40" + "1" - x  
390

JavaScript allows a variable to modify its type based on the context of its usage. A script demonstrating the ability of a variable to change types is shown above.

  • The variable x is initially defined as a number.
  • Applying the NOT operator twice converts the variable to a boolean type.
  • Next, the variable is cast to a string and concatenated with another string.
  • Applying the NOT operator twice casts the string to a boolean
  • Performing an addition casts the boolean to an integer
  • Finally, two strings are concatenated and then converted to an integer to perform subtraction.

Vulnerabilities in JavaScript

The ability to dynamically add and update members in JavaScript results in potential runtime errors. There are several types of runtime errors in JavaScript:

  • applying an arithmetic operator to an object that is not a wrapper object for a number,
  • accessing a non-existent variable, and
  • accessing a property of a null object.

The cause of many runtime errors in JavaScript is due to invalid type conversions and the lack of classes

> "hello " - "world"  
NaN
> "5" / "one"  
NaN
> "true" & false  
0
> "true" - false  
NaN

Type conversion errors occur in JavaScript when arithmetic operations are performed on non-numeric values. If operands cannot be cast to numeric types, a runtime error is thrown. As illustrated in code snippet above

  • The first operation attempts to subtract two strings, resulting in an invalid number.
  • The second operation attempts to divide two strings representing numbers, but is invalid because the second string is non-numeric.
  • The third operation demonstrates the unpredictability of type conversions, since boolean values are provided and an integer type is returned.
  • The last operation shows another example of unexpected behavior, because the expected result of 1 is not returned.

Many of these errors could be flagged before execution using a type-checking tool. Runtime errors can occur in JavaScript due to the lack of enforcement of type checking and function parameters. The number of formal parameters in a function definition need not match the number of actual parameters. If there are too few parameters, the remaining parameters are taken to be undefined.

Prototype-Based Errors

JavaScript is a prototype-based language that supports the dynamic addition of members to objects. Runtime errors occur when a script references an undefined variable or non-existent member of an object.

> var obj = { x:1 } 
> print(obj.x)
1
> print(obj.y)  
undefined
> obj["undefined"] = "should be undefined" 
> print(obj[obj.y])
should be undefined

A prototype-based error in JavaScript is shown in code snippet above.

  • An object is created with a single member variable. The value of the member variable is then printed.
  • Then the script attempts to access the member variable y, which is undefined.
  • Next, the script modifies the value of undefined, which is used incorrectly in the last line.

JavaScript is a classless programming language. Despite this property, one of the most common idioms found in contemporary JavaScript applications is the emulation of class-based object orientation through the prototype system

The lack of type safety in JavaScript presents security vulnerabilities. Listed below are the most common JavaScript threats that have been incrementally documented and derived from real world attacks:

  • ClickJacking & Phishing by mixing layers and iframe
  • CSRF and leveraging CORS to bypass SOP
  • Attacking WebSQL and client side SQL injection
  • Stealing information from Storage (Local/Cookie) and Global variables
  • Cross Site Scripting (XSS) and HTML 5 tag abuse
  • HTML 5/DOM based XSS and redirects
  • DOM injections and Hijacking with HTML 5
  • Abusing thick client features
  • Using WebSockets for stealth attacks
  • Abusing WebWorker functionality
  • Using components with known vulnerabilities

In the next part of this series we will examine challenges in server side Javascript applications.


An Oxymoron : Static Analysis of a Dynamic Language (Part 1) was originally published in ShiftLeft Blog on Medium, where people are continuing the conversation by highlighting and responding to this story.


*** This is a Security Bloggers Network syndicated blog from ShiftLeft Blog - Medium authored by Chetan Conikee. Read the original post at: https://blog.shiftleft.io/an-oxymoron-static-analysis-of-a-dynamic-language-part-1-9ac5cdc158c1?source=rss----86a4f941c7da---4