Cross-site scripting (XSS) attacks are a type of injection attack in which a malicious script may be injected into the document and executed. According to
:
“Cross-Site Scripting (XSS) is a misnomer. The name originated from early versions of the attack, where stealing data cross-site was the primary focus. Since then, it has extended to include injection of basically any content, but we still refer to this as XSS. XSS is serious and can lead to account impersonation, observing user behaviour, loading external content, stealing sensitive data, and more.”
Suppose that you built an application that takes some input from the user and then displays it as an h1. This is being done by setting the inner HTML of the h1 to the input’s value on the button click, using jquery:
Take user input and set it as inner HTML
What if instead of some string, the user injects JavaScript code in the input box using the HTML <script> tag?
User inputs some js code and😲 😲 😲
As you can see, the JavaScript code injected by the user got executed. In the example above, it was just a simple alert statement, however, an attacker can use this to execute any malicious code that he wants, such as hijacking the user’s session by getting hold of the cookie, the local storage, and sending it to his own server via an HTTP request:
Most modern web browsers and frameworks such as react have built-in mechanisms to protect against injecting JavaScript by setting it as inner HTML using the script tag. Most likely, the js code will be ignored. However, there are many other ways to perform this attack, such as using the “
onerror”, “onclick” and “onmouseover”
HTML attributes:
You might be wondering: So what if the user is able to execute some JavaScript code on my website? After all, he can anyway do this through the console. Why should I worry about preventing the user from running his own scripts on my website, stealing his own cookies and data that he already has?
The problem arises when the user input is going to your backend server, where it may also be stored in some database, from where it may be retrieved by other users.
Let’s say that this is some post on a Reddit-like application:
Now consider this scenario: Attacker
A
injects some script by giving it as the user input and the script now gets stored in your database.
Now some user
B
who wants to look at
A
’s post will unwittingly be executing the script injected by
A
that was fetched from the database. This way, all the user’s who view that post will get hacked!!! This is called a stored XSS attack.
Stored XSS attacks:
The injected script gets stored on a server and retrieved and executed by the victim(s) when it tried to access the stored data.
Reflected XSS attacks:
What if the attacker gets the victim to somehow inject a script into the website instead of the attacker having to do so?
Let’s take the above example where the user’s input was getting sent to the server and then reflected back to the user and displayed on the DOM by setting inner HTML. The attacker can send a link to his target via email or social media or other social engineering methods. The email contains a link:
When the victim clicks this link, an HTTP request is sent to the server of yourwebsite.com where a post will be created that has the malicious script as the data. This post will then be reflected back to the victim (or other users who want to view this post) and when your website tries to set the inner HTML, the injected script will get executed and the victim’s cookie will be sent to the attacker’s server. 😵 😵 😵
How to defend against JavaScript injection attacks:
We can easily prevent an attack in the first example by setting the user input as text, not HTML:
Similarly, for react, try to avoid using the dangerouslySetInnerHTML attribute to set inner HTML.
But there might be some scenarios in your application where you would need to set inner HTML. To defend against the user injecting a script into your website in such a case, you can “
sanitize”
the user input before setting it as HTML. Sanitization will render any scripts that may be hidden inside the HTML useless. A popular npm package that helps you do that is
.
In addition to this, we can also have mechanisms on the server-side to prevent such attacks. One popular way to do this is to use the “
Content-Security-Policy
” HTTP header. According to
:
“CSP makes it possible for server administrators to reduce or eliminate the vectors by which XSS can occur by specifying the domains that the browser should consider to be valid sources of executable scripts. A CSP compatible browser will then only execute scripts loaded in source files received from those allowed domains, ignoring all other scripts (including inline scripts and event-handling HTML attributes).”
So, on your server-side, you can add this header to the HTTP response where you are sending your HTML files, such as index.html, this will mean that your document (HTML) will not execute scripts that aren’t from yourwebsite.com:
*note that js injection attacks may still be possible if the js that you are returning from your whitelisted domains is itself compromised.
You can also set the “
connect-src”
attribute of CSP to specify what all domain requests can be sent to by your scripts so that requests are not sent to the attacker’s domain. Here is the error I got in the console when I tried to send a request using the fetch API to example.com:
*note that the attacker can still steal your cookies (or other information) using:
. Here is a list of all
. Remember that the CSP checks are enforced by the browser, so if a user disables this check in her browser, she will be vulnerable to attacks.
Ok, so you have sanitized user inputs before setting them as HTML, you have also set the CSP headers in your document responses, and you have made sure that the scripts coming from your whitelisted domains aren’t compromised, is your website completely safe from XSS attacks now?
Not quite, scripts can still be injected via other sources, such as 3rd party libraries and packages, browser plugins or third-party advertisements! Make sure to use npm audit to audit your dependencies for known security issues.
Here is a fun way to practice XSS attacks before you progress towards trying to attack Medium:
.
Software Developer @Microsoft. Writer @BetterProgramming. Programming for more than 7 years. Learning and writing about new technology every day. Love to read. Check out https://akshaydagar.netlify.app.