Cross site scripting, one of the most common security vulnerabilities that exists, is a powerful web exploitation technique where malicious JavaScript code is given to web frontends that will execute it for other users. In a way, you can mostly think of XSS as ‘JavaScript injection’, although not all attacks necessarily involve JavaScript!

Same-Origin Policy

XSS is basically a way to circumvent the *same-origin policy, * a security mechanism that basically aims to stop one website from reading/writing to the DOM of another URL of a different origin. For example, the JavaScript code in is prevented from reading/writing to Note that this restriction only applies to scripts, not HTML tags like images, making them an avenue for exploits!

A web content origin is the combination of the protocol, hostname and port of a URL. We say that two URLs are of the same origin if all 3 parts match. For example, and are the same origin.

Same origin example
Different origin example

Note: when one origin reads/writes to another different origin, we call this interaction cross-origin.

Since the same-origin policy stops you, an attacker, from running your malicious javascript on, you would have to get to run the malicious javascript for you — this is what XSS is! How would you do this? There are a few common patterns for how XSS attacks are carried out in CTFs and in real hacks, the two discussed in this article will be reflected XSS and stored XSS.

Just remember, the browser will blindly trust and render all the HTML, and consequently run all the JavaScript embedded in that HTML, that returns.

Reflected XSS (Non-Persistent XSS)

Reflected XSS is possible when the malicious input is reflected back in the HTML returned in the response. You perform this style of attack by adding the malicious JavaScript in the URL, which when requested from the web server, will result in the web server sending back a document that runs that malicious JavaScript when the browser renders it.

One common example used to explain how this is achieved is when a website lets you input a search query in the URL and then renders your search query in the DOM itself. For example, it might look like this:

Reflected XSS example

If the query value gets directly rendered without any validation, then we’re able to put in whatever HTML we want and have that be rendered. The most traditional thing to try is to try to put in a <script>alert(1)</script> into the URL, like:<script>alert(1)</script> . Note that you would need to encode the URL to produce something like: .

Then, you can distribute this URL to unsuspecting users (commonly through legit-looking emails), and then when the link is clicked, their browser will execute your malicious code which could do anything from displaying a harmless alert or attaching their cookies and sending it to a web server you control.

Stored XSS (Persistent XSS)

When the input is reflected back in the reponse and stored in the database itself, meaning that it’ll potentially continue to be distributed to users, making it an order of magnitude more disastrous than reflected XSS. Additionally, one challenge with reflected XSS is that you have to bait victims into clicking a link to get the malicious javascript to run. With stored XSS, any user that visits the site itself will execute that malicious javascript.

The classic example used to explain how stored XSS is achieved is when websites let you make comments.

Comment box

In this example, as an attacker, you could send in a <script>alert(1)</script> as the comment body which gets stored in the application’s database. Then, when other users visit the page where your comment is fetched and put into the HTML response, your malicious code will run in their browser!

Note that <script> elements aren’t the only way to execute XSS, you can attach your malicious JavaScript to event handlers like [onload]( on regular HTML elements like body , svg , etc.

Defending against XSS

The main defense against XSS is to sanitise user input on arrival. Often, the best way to do this is via tried and tested input sanitising libraries like DOMPurify.