What can be used to run JavaScript in a restricted environment

I'm working on an HTML5 game, and as one of the features, I'd like the more advanced users to be able to write short scripts in javascript that can be run by objects in the game. The problem is that I'd like to restrict what the player scripts can do so they can't open new windows, cheat, modify the nature of the game, etc. I would, however, like to expose certain methods to the scripts so they can make useful things.

A few people have suggested that I could do this with Web Workers. Others have told me that I need to build an interpreter inside javascript to do this. Obviously, I can't just use eval, so how could I do this and what would be the best way, if there is a 'best' way?

This is post # 21 of the series, dedicated to exploring JavaScript and its building components. In the process of identifying and describing the core elements, we also share some rules of thumb we use when building SessionStack, a JavaScript application that needs to be robust and highly-performant to help companies optimize the digital experience of their users.

Overview

Cross-Site Scripting (XSS) is a browser-side code injection attack. An injection attack is performed when the attacker is able to inject malicious code into an application. This code is then executed by the particular environment and performs malicious actions.

In the case of the browser, the attacker is injecting malicious scripts inside of a web app, which is being used by the victim. In general, XSS attacks are based on the victim’s trust in the legitimacy of the web app they use.

JavaScript runs in a restricted environment that has limited access to the user’s operating system. This means that XSS attacks are not intended to damage the computer of the victim. Their goal is mainly to steal personal information.

Depending on the goals of the attacker, XSS can be implemented in a number of different ways. There are five main types of XSS attacks.

Persistent (Stored) XSS

Persistent XSS is possible when a web app takes user input and stores it into its servers. When the application doesn’t perform proper front-end and back-end validations before storing the data, it exposes serious vulnerabilities. When the web app loads the stored data afterward and embeds it into the HTML response pages is the moment when a potential code injection is possible.
These types of attacks are less frequent because the vulnerabilities that make them possible are less common and difficult to find.
On the other hand, they are highly impactful. The reason is that once the malicious data is being stored on the web app’s servers, it can potentially be served to many users. The users don’t need to click on malicious links or anything else — the malicious code is already embedded in the app itself.

Persistent XSS attacks are Type 2 XSS attacks because the attack is carried out via two requests:

  1. Injecting malicious code and storing it on the web server.
  2. Loading the stored code and embedding it into the HTML pages that contain the payload.

Certain types of websites and web apps are more prone to the vulnerabilities that are required by Persistent XSS attacks because they allow users to share content. Common examples are social networks and forums.

Example

Suppose that an attacker identifies vulnerabilities in the comment functionality under a post in a social network. The vulnerability is that the social network renders the raw input from the comments inside of the HTML on the page.

Persistent XSS attack on a social network

This allows the attacker to add a custom script into his comment:

When the social network loads this comment, it will include the `script` tag into its HTML. This will automatically redirect the current user to the URL of the malicious website and will send all of the cookies as a query parameter. The malicious website can then store the cookies and steal sensitive data.

Since many people can visit the comment section of a particular post, all of them will be victims to the attacker.

This particular case is very superficial, and there hopefully aren’t any serious social networks out there with such easy to find vulnerabilities, but it illustrates the potential scale of Persistent XSS.

Prevention

The most effective way to prevent Persistent XSS attacks is to make sure that all user input is properly sanitized before it is being stored on the servers.

Sanitizing static content is also a great practice since malicious scripts can be injected in various ways.

There is almost nothing reliable you can do purely on the client-side to prevent Persistent XSS attacks.

Reflected XSS

Reflected XSS attacks occur when the data, which is sent from the browser to the server is contained in the servers’ response.

These attacks are called “reflected” since malicious scripts are reflected off of a web app to the victim’s browser.

The script is activated through a link, which sends a request to the web app with a vulnerability that enables the execution of malicious scripts.

Unlike Persistent XSS attacks, where the malicious script is being stored on the servers of the web app, Reflected XSS attacks only require that the malicious script is embedded into the URL.

While Persistent XSS attacks perform the malicious scripts automatically to any user, who visits a page with such a script, the Reflected XSS attacks require the end-user to click on the malicious link.

Example

Suppose that a web app has search functionality. This is how the web app works:

  • The user types a search term into an input field.
  • The web app redirects the user to a “results” page which has the search term as a query parameter.
  • The search term is taken from the query parameter and is being sent to the server for processing.
  • When the processing is complete, the response is rendered on the page. The response contains both the search term of the user and the matched results from the server.

If the user types in “javascript” into the input field, a redirect will happen to the following page:

https://example.com/search/?term=javascript

The web app takes the “javascript” term and sends it to the server. Once the processing is done, the page renders the response:

If the web app doesn’t perform any processing or validation on the search terms, the attacker can embed malicious code into the search term by providing the following input:

The generated URL will be:

https://example.com/search/?term=<script>/*+Malicious+code+*/</script>

And the page will then render:

If this URL is spread to other users, the supplied script by the attacker will execute in their browser.

Reflected XSS attack

There are various means by which an attacker can spread the malicious URL to victims. These include placing links on websites controlled by the attacker, or websites that allow content to be generated (forums, social networks, etc.), sending the link in an email, etc. The attack could target a particular user or it can be an indiscriminate attack against any user.

Prevention

Unlike Persistent XSS attacks, users can avoid Reflected XSS attacks by being vigilant.

Specifically, this means not clicking on suspicious links that may contain malicious code.
As with Persistent XSS, sanitizing all of the user input is mandatory for the prevention of Reflected XSS.

Self-XSS

Self-XSS is quite similar to Reflected XSS. The difference is that Self-XSS cannot be triggered via a specially crafted URL. Self-XSS can only be triggered by the victim themselves in their own browser.
This might sound like Self-XSS attacks are not dangerous. This is far from true. Self-XSS attacks are successfully delivered through social engineering. In the context of information security, social engineering is the psychological manipulation of people into performing actions that will potentially have negative consequences for them.

Example

A common approach to Self-XSS attacks is making the victim paste some malicious code into their browser’s console. This gives the attacker access to all of the currently available information from the cookies, DOM, etc.

Self-XSS attack

Prevention

Self-XSS attacks can only be prevented by user vigilance. The developer of the web app cannot detect or block the execution of malicious code in the browser’s console.
Some popular web apps and websites are putting warning messages in the browser’s console to prevent users from executing any code there.

Browser vendors have also taken steps to mitigate this attack by implementing safeguards to warn users about Self-XSS attacks.

DOM-based XSS

DOM-based XSS attacks are performed when the DOM of a web app is dynamically modified and malicious code is injected by the web app itself during runtime.

In order for the DOM-based XSS to happen, the JavaScript code of the web app needs to take input from a source that is controllable by the attacker, such as the URL in the browser’s tab.

Example

Let’s take a look at the following page:

The script in the page takes the value from the `role` query param and inserts it into the DOM.

The attacker can set the value of the query parameter to a malicious code, which will be injected into the DOM:

https://example.com/?role=<script>/*Malicious+code*/</script>

DOM-based XSS Attack

Even though the code of the web app is vulnerable to this attack, in this particular case, the server can detect it since the URL is part of the request. If there are security mechanisms built into the server, the attack will fail.

The technique to avoid sending the payload to the server is using URL fragments. This is the part of the URL after the `#` symbol. URL fragments are not sent to the server by the browser.
The previous URL can be modified to:

https://example.com/#role=<script>/*Malicious+code*/</script>

This way, the malicious script won’t reach the servers and it won’t be detected.

Prevention

All of the DOM manipulation and redirects which depend on user input should be sanitized. The sanitization should happen on the client-side since DOM-based XSS cannot be prevented on the server.

In some cases, user vigilance can also play a role in this type of XSS attack. In cases similar to our example, where the user has to click a URL or they need to enter some data, they should be cautious about entering malicious code.

Blind XSS

Blind XSS attacks are a type of Persistent XSS attacks. They are executed in the same manner.

The difference is that the malicious code is rendered and executed in another part of the application or in a completely different application. In both cases, the attacker has no access to the page which will execute the malicious code.

Example

An example of a Blind XSS attack is when an attacker injects malicious code into a customer feedback page of a web app. When the web application’s administrator opens the feedback dashboard, the malicious code will be executed. This can even be in a different application, such as an internal tool for managing user feedback.

The attacker’s malicious code can be saved by the server and only executed after a long period of time when the administrator visits the vulnerable dashboard page. It can take hours, days, or even weeks until the payload is executed.

Blind XSS attack

Another example of Blind XSS attacks is with logging solutions such as exception handlers. The attacker can use the API of the logger, to log some malicious code instead of an error. In the dashboard of the exception handling solution, where the logged errors are displayed, the malicious code will be rendered and executed.

Prevention

Since Blind XSS attacks are a subset of Persistent XSS attacks, the preventing methodologies are the same.

While building SessionStack we have taken into account the potential risk of Blind XSS. The reason is that once you integrate SessionStack into your web app, it starts collecting data such as DOM changes, user interactions, JavaScript exceptions, stack traces, network requests, and debug messages. This data is then processed and allows you to replay user journeys as videos in order to optimize product workflows, reproduce bugs, or see where users are stuck.

It’s easy for attackers to try to abuse our JavaScript APIs and try to send malicious data instead of the standard events that are being collected. We have built extensive client-side and server-side sanitization and filtering mechanisms in order to detect such attacks. This guarantees that our users won’t be impacted by malicious scripts that have been submitted by attackers.

There is a free trial if you’d like to give SessionStack a try.

SessionStack replaying a user session

If you missed the previous chapters of the series, you can find them here:

Resources: