Saturday, July 13, 2024

Preventing Cross-site Scripting (XSS) Web Security

Cross-site scripting is one of the most common and popular web attacks.

XSS is a command injection of the client side, it can result in any action that can be performed by the user. Mostly XSS is used for session hijacking where the attacker using javascript makes the victim transmit session cookies to an attacker-controlled server and from there the attacker can perform “session riding”.

However, XSS can also result in a complete application takeover. Consider a scenario in which you inject javascript and it gets stored. The admin then loads that into a web browser (usually logs or CMS). If an XSS is present there you now have the admin session tokens. That is why XSS can be very dangerous.

Consequences and impacts can vary depending on the type of attack and the context in which the attack is launched.

XSS itself is dangerous in the following sense:

  • Session ID/ cookie can be theft to gain full access to victim accounts.
  • Temporary Defacement of the website. Running malicious JS scripts (miner, card data stealer, key-logger etc)
  • Using exploitation frameworks like Beef an attacker can do some OS calls such as opening webcams remotely, turning the microphone on, redirecting all websites to malicious websites etc.
  • Sometimes XSS can be used to steal victims’ secret tokens, cookies, CSRF (cross-site request forgery) tokens, API keys and then further exploitation can be done such as CSRF attacks.
  • Used in phishing attacks, an attacker could use a trusted domain that is vulnerable to XSS, to Redirect/Forward an unsuspecting user to a malicious website.

XSS can be typically* classified as:

  • Self-XSS / Reflected XSS / Non-persistent XSS / Type-II XSS
  • Persistent XSS / Stored XSS / Type-I XSS
  • DOM based XSS
  • mXSS (covered in the following extended: cheatsheet)


Let’s suppose we have the following PHP code – PHP just to pick (a legible) one:

  $id = $_GET['id'];
  // ... some more code :-)

  // Simple XSS example:
  //   $_GET['id'] is left unvalidated/unsanitised
  //   and later echoed into the HTML as-is.
<!DOCTYPE html>
<!-- Some more html -->
<script type="text/javascript">
  function getLink() {
    var link = "<?= $id; ?>";

Textbook XSS: The victim opens a link that looks like:

GET”; alert(1); var a = “)

and, magically, the code above, after being rendered by PHP and sent to the victims browser, will look like:

  The following request is made: 
    GET"; alert(1); var a = "
  This is the generated output
<!DOCTYPE html>
<!-- Some more html -->
<script type="text/javascript">
  function getLink() {
    var link = ""; 
    var a = "";

In this case we’re just opening a popup and displaying “1” inside of it, but an attacker could get much more creative: he could make an AJAX call and send document.cookie to his server (provided the application didn’t set a HTTP-Only flag to the session cookie).

Recommended:  ThingsBoard 3.3.1 XSS - Stored Cross-Site Scripting (XSS)

It’s called reflected XSS since you’re sending a malicious request and getting some malicious response back.
Bear in mind: the victim would need to open a maliciously crafted link (which could also be shortened using third party link shortners sites)

Persistent XSS

Similar to a Self-XSS, but different workflow: the $id in this case has been stored into a database (or other persistent storage) and then read from it at a later time by the victim(s).

A classic example here would be a platform that allows users to post content that is then displayed to other users (i.e. Medium): an attacker would be able to write some JS into a post and any user reading that post would automatically (perhaps silently) run the malicious JS code.

You can see how that can go very wrong very quickly, making persistent XSS a much more dangerous sub-category of this sort of attack.

DOM based XSS

Firstly described by Amit Klein in 2005, DOM based XSS is less known variant and occurs whenever the XSS is a direct result of modifying the DOM context in which the regular client-side code runs, so that the execution results in an “unexpected” behaviour.

var pos=document.URL.indexOf("name=")+5;
Welcome to our system

Normally this HTML page would be used for welcoming the user:


however a request such as:


would result in an XSS condition.

Again – you could escalate this: send the cookies to a malicious server – potentially holding session info (i.e. session cookie) and take over an account.

At the core, an XSS vulnerability is present whenever an application accepts untrusted (anything the application receives from the outside — usually user input data) data and displays it without correctly escaping it depending on its context.

Context is a fundamental word in that sentence and that is also why an XSS isn’t much different from a SQL or XML/HTML injection.

What can an attacker do?

Generally speaking, if an attacker manages to perform an XSS attack, he could be able to basically do one or more of the following:

Ad-Jacking — Inject his ads in it to make money
Click-Jacking — Create a hidden overlay on a page to hijack clicks of the victim to perform malicious actions.
Session Hijacking — HTTP cookies can be accessed by JavaScript if the HTTP ONLY flag is not present in the cookies.
Content Spoofing — JavaScript has full access to client side code of a web app and hence he could use it show/modify desired content.
Credential Harvesting — He could use a fancy popup to harvest credentials: WiFi firmware has been updated, re-enter your credentials to authenticate.
Forced Downloads — So the victim isn’t downloading his malicious flash player from No problem: he could just try to force a download from the trusted website the victim is visiting.
Crypto Mining — He could use the victim’s CPU to mine some bitcoin (or altcoin)
Bypassing CSRF protection — He could collect and submit CSRF tokens to perform POST operations elsewhere on the site.
Keylogging — Self-explanatory
Recording Audio, Taking pictures, Geo-location — It requires authorization from the user but he could access victim’s camera, microphone and location. Thanks to HTML5 and JavaScript.
Stealing HTML5 web storage data — A website can store data in the browser for later use and, of course, JavaScript can access that storage via window.localStorage() and window.webStorage()
Fingerprinting — Very easy to find your browser name, version, installed plugins and their versions, your operating system, architecture, system time, language and screen resolution.
Network Scanning — Victim’s browser can be abused to scan ports and hosts with Javascript.
Crashing Browsers — …
Stealing Information — Grab information from the webpage and send it to his malicious server.
Redirecting —Self-explanatory
Tabnapping — Fancy version of redirection: when no keyboard or mouse events have been received for more than a minute, it could mean that the user is afk and he can sneakily replace the current webpage with a fake one.
Capturing Screenshots — Thanks to HTML5 again, now you can take screenshot of a webpage. Blind XSS detection tools have been doing this before it was cool.
Perform Actions — He is controlling the browser

Not all is lost, cross-site scripting can be prevented!

Recommended:  NMAP - User Agent - How to Set Your Own, Permanently!

What can be done?

Unfortunately, there’s no silver bullet. You could be:

  • using a Web Application Firewall (and hope for the best: they can block some/most common attacks but not all)
  • using X-XSS-PROTECTION that stops pages from loading when the browser detects reflected cross-site scripting (deprecated IE feature)
  • using Content Security PolicyX-FRAME-OPTIONS, (etc..) headers to limit impact

but again, none of the above (or a combination) are solutions to the actual problem. If anything they might give you a false sense of security.

The real solution is to write clean code 😃.

As we said earlier:

At the core, an XSS vulnerability is present whenever an application accepts untrusted (anything the application receives from the outside — usually user input data) data and displays it without correctly escaping it depending on its context.

So, the solution is to correctly escape content based on the context they live in: in a generic HTML context <,> have special meanings so they should be coverted into their HTML code equivalents (&lt;&gt); in a HTML attribute context, instead, double quotes (or single quotes) have special meaning so they should be converted into their HTML code equivalents (&quot;), etc…

In Javascript, instead, depending on the specific area in which the untrusted input is injected, it can be a bit trickier to understand what characters have special meaning – so, in that instance, it’s easier to escape all non-alphanumeric characters with their hex equivalent (i.e. double quote becomes \x22, $ becomes \x24, etc…).

Don’t roll your own security: you should instead rely on whatever your language-of-choice / framework provides for this.

Recommended:  WATCH: Top 5 CyberSecurity Trends To Expect In 2023

Native — C# (ASP.NET)

The Razor engine used in MVC automatically encodes all output sourced from variables. You could also use JavaScriptEncoder to encode variables in Javascript context.

Native — Go (Golang) escapes special characters (<>&’ ”) to their HTML code equivalents gives you more control and better escaping functionality for CSS/JS/HTML.

Native — Java

OWASP provides AntiSamy (an API for ensuring user-supplied HTML/CSS is in compliance within an application’s rule)

OWASP also provides Java-HTML-Sanitizer that pretty much does the same thing but in a different way. HTML-Sanitizer is fast and simpler to use but less flexible.

OWASP also used to provide ESAPI but that’s now a defunct project.

Native — PHP

htmlentities will convert all applicable characters to HTML entities, but that’s not the best way to prevent XSS.

htmlspecialchars here is a simple practical fix with the implementation of htmlspecialchars in PHP.

echo $_COOKIE["tc"]; - Vulnerable
echo htmlspecialchars($_COOKIE["tc"], ENT_QUOTES, 'UTF-8');- Patched

HTMLPurifier will by default strip malicious code, but you can configure it to escape it instead.

Native — Python

The html package allows you to convert &<> (and “ ‘ if requested) to their HTML-code equivalents.

Angular 2,3,4,5,6,7,8,9 — JS

Angular treats all values as untrusted by default. When a value is inserted into the DOM from a template, via property, attribute, style, class binding, or interpolation, Angular sanitizes and escapes untrusted values.

Django — Python

If you use Django templates you are protected against the majority of XSS attacks. However, it’s not foolproof: Django templates escape specific characters which are particularly dangerous to HTML but it won’t protect you from HTML attribute based attacks for instance.

Flask — Python

Flask configures Jinja2 to automatically escape all values by default.

Laravel — PHP

In Laravel ≥5 , the default {{ }} should escape all output

React — JS

React automatically escapes variables for you, but there are a few gotchas. More about that here.

Symfony — PHP

Symfony applications are safe by default because they perform automatic output escaping

Still not satisfied? Check out this extended XSS Cheat Sheet

Suggest an edit to this article

Go to Cybersecurity Knowledge Base

Got to the Latest Cybersecurity News

Go to Cybersecurity Academy

Go to Homepage

Stay informed of the latest Cybersecurity trends, threats and developments. Sign up for our Weekly Cybersecurity Newsletter Today.

Remember, CyberSecurity Starts With You!

  • Globally, 30,000 websites are hacked daily.
  • 64% of companies worldwide have experienced at least one form of a cyber attack.
  • There were 20M breached records in March 2021.
  • In 2020, ransomware cases grew by 150%.
  • Email is responsible for around 94% of all malware.
  • Every 39 seconds, there is a new attack somewhere on the web.
  • An average of around 24,000 malicious mobile apps are blocked daily on the internet.
Share the word, let's increase Cybersecurity Awareness as we know it
- Sponsored -

Sponsored Offer

Unleash the Power of the Cloud: Grab $200 Credit for 60 Days on DigitalOcean!

Digital ocean free 200

Discover more infosec

User Avatar
Just your average information security researcher from Delaware US.

more infosec reads

Subscribe for weekly updates