JavaScript is now the world’s most widely used programming language, found in almost every web application out there. JavaScript vulnerabilities are therefore a major web security risk and understanding them is vital for preventing data breaches and other security incidents.
What makes a JavaScript vulnerability?
According to recent research, JavaScript is actively used by nearly 14 million developers, making it the most popular software development language in the world and by far the most widely used language in web development. This makes understanding JavaScript vulnerabilities a crucial part of ensuring web application security, especially considering that attacks in the application layer are now the top cause of external breaches. But what exactly makes a JavaScript vulnerability?
In a broad sense, any JavaScript code that leads to a security vulnerability can be considered a JavaScript vulnerability. If you have a full-stack JavaScript setup, from Node.js on the server to a framework like AngularJS or React on the front end, that definition might cover all security issues in your application. This is important to keep in mind when building security awareness among developers since all web vulnerabilities from the OWASP Top 10 (and beyond) can be introduced through insecure JavaScript code. But then is SQL injection really a JavaScript vulnerability?
It’s all about cross-site scripting
From a web security standpoint, it’s more useful to think of JavaScript vulnerabilities as opportunities for the attacker to control script execution. First and foremost, that means cross-site scripting (XSS) in its many shapes and forms. Based on Netsparker scan results from many years of scanning real-life applications, we can confidently say that XSS accounts for over two-thirds of all web application security vulnerabilities. In other words, the most common JavaScript vulnerabilities are all different types of cross-site scripting.
We have a separate article about the different types of cross-site scripting, but to recap, there are the 3 main types:
- Reflected XSS: Malicious script code entered by the attacker (for instance, as a search query) is accepted by the server. The code is then inserted into the HTML on the relevant page and served back to the browser, where it is executed.
- Stored XSS: The server accepts user input that includes malicious code and stores it. For example, the attacker could put code in a user profile description that gets stored in a forum database. When another user later loads that profile page, the malicious script is executed.
- DOM-based XSS: Attacker-controlled inputs processed entirely in the user’s browser are used to modify the current page and insert malicious code using Document Object Model (DOM) manipulation. Because everything happens on the client side, there is no malicious code in either the original HTML page or the server response.
JavaScript security issues shifting to the client side
JavaScript started out as a way to make static web pages more interactive and responsive, with most application logic and processing being implemented on the server side in another programming language. As dynamic websites grew into full-fledged web applications, they started shifting more and more processing to the user’s browser for a desktop-like experience. With HTML5 adding local storage, it is now possible to have entire single-page applications that load once from the server and then run completely on the client side, exchanging only occasional requests with the server.
All this contributes to the growing impact of JavaScript vulnerabilities in modern web applications. Increased reliance on single-page applications is exposing ever more application data to client-side JavaScript. When combined with insecure design or implementation choices, this can lead to sensitive data being extracted through DOM-based XSS attacks that often leave no trace on the server.
Detecting cross-site scripting vulnerabilities
Reliably detecting XSS vulnerabilities with automated tools is not a trivial task, especially when you consider that only a small part of a modern web application is custom code – as much as 70% can be framework code and open-source JavaScript libraries such as jQuery. To test the entire application as executed and closely simulate attacker interactions, you need dynamic application security testing (DAST) with an advanced security tool such as Netsparker that includes a full web browser engine. This is particularly important for DOM-based XSS, where you can’t check server responses for malicious JavaScript because the whole attack takes place on the client side.
Netsparker comes with a wide array of security checks to reliably find many types of cross-site scripting vulnerabilities, including DOM-based attacks. It also has a Technologies feature to identify components with known vulnerabilities. The Netsparker scanner incorporates over a decade of continuous research and development by top security professionals and is regularly updated to include the latest attack techniques.
Preventing JavaScript vulnerabilities
The risk of XSS exists whenever your application handles user input. As with so many vulnerabilities, proper input validation with context-sensitive data encoding is always the best starting point for limiting an attacker’s options. Note that input filtering alone is not enough to prevent XSS and should only be used as part of a defense-in-depth.
Secure coding practices for preventing JavaScript vulnerabilities include treating all data sources as untrusted by default and avoiding potentially insecure JavaScript functions such as eval() wherever possible. To minimize the risk of DOM-based XSS, you should never use dangerous properties such as innerHTML when manipulating DOM element content. Choosing a secure framework and learning to use it correctly also plays a big part in avoiding insecure constructs that may leave the application open to attack.