Chapter 32 Β· Cross-Site Scripting

The Script That Wore a Disguise

Priya, a web security consultant, walks a development team through the attack that exploits the one thing no firewall can fully protect: the browser's unconditional trust in the websites it visits.

Why XSS Is Different

Priya opens the session with a question: "Who do you trust more β€” a stranger on the street, or your bank's website?" The developers all agree: the bank's website. "That's the problem," she says. "Your browser trusts websites too. And attackers know it."

She explains the fundamental concept: Cross-Site Scripting β€” abbreviated XSS, not CSS, because Cascading Style Sheets claimed that abbreviation first β€” is not an attack on the server. It's an attack on the user, delivered through the server. The attacker's goal is to get their script running in the victim's browser, and they achieve it by hiding that script inside a trusted website the victim already believes is safe. The browser sees script from the trusted site's domain β€” or what appears to be the trusted site's domain β€” and executes it without hesitation. "The attacker doesn't need to break into the server," Priya says. "They just need the server to pass their code to the victim's browser. And a lot of servers will do exactly that if the developer isn't careful."

πŸ’‘ What XSS Exploits XSS exploits the trust a browser places in the websites it visits. When code appears to originate from a trusted domain, the browser executes it without suspicion. Attackers use this trust to run their own scripts in the victim's browser by embedding them in β€” or delivering them through β€” a legitimate website. XSS attacks the user, not the server.

JavaScript β€” The Weapon of Choice

Priya opens a browser and shows the team a simple demo. "What makes XSS practical is JavaScript. Every browser supports it. Nearly every user has it enabled. And JavaScript running in a browser can read session cookies, capture keystrokes, redirect the user, make requests on their behalf, and send data anywhere the attacker chooses β€” all silently, all without the victim seeing anything unusual."

She draws the basic attack chain on the whiteboard. The attacker crafts a malicious script. They find a way to get that script into a page the victim will view β€” either by embedding it in a link or storing it on the server. The victim visits the page or clicks the link. The browser loads the page, encounters the script, and executes it β€” because the script appears to come from the trusted site. The script runs silently, collecting cookies, session IDs, or credentials, and transmitting them to the attacker. "The victim has no idea," Priya says. "They visited their normal website. Nothing looked wrong. But their session is now in someone else's hands."

πŸ’‘ Why JavaScript JavaScript is the most common XSS vector because it is universally enabled, runs in every browser, and has access to everything the browser holds for a page β€” cookies, session tokens, form data, DOM content. A malicious script can silently extract and transmit all of this. Unlike server-side attacks, the target is the client, and the client's own browser does the attacker's work.

The Reflected Attack β€” One Link, One Victim

Priya explains the first type: non-persistent XSS, also called reflected XSS. "Imagine a site has a search box. You type 'laptops,' the page shows results, and in the header it says: You searched for: laptops. Where does that 'laptops' come from?" The team confirms: from the URL, passed back from the search input. "Right. Now what if instead of 'laptops,' the URL contained a JavaScript tag? If the site puts whatever's in the URL directly into the page without checking it, the browser sees a script and runs it."

The attack flow: the attacker finds a vulnerable search field or URL parameter on a legitimate site. They craft a malicious link β€” the legitimate site's URL with a script embedded in the query parameter. They email this link to a target. The target clicks the link (it looks like it's going to a real website they know). The legitimate site's server reflects the script back in the response. The victim's browser executes the script, which sends their session cookie to the attacker's server. "The key word is reflected," Priya says. "The attacker's code hits the server, the server bounces it back, and the victim's browser fires it. The server is innocent β€” it just passed along what it received without checking. The victim is the one who pays."

πŸ’‘ Reflected XSS Non-persistent (reflected) XSS embeds a malicious script in a URL. The server reflects it back in the response without storing it. The attacker sends the crafted link to a specific target β€” usually via email. The victim's browser executes the script as if it came from the trusted site. Session credentials or cookies are sent to the attacker. Each attack is delivered to an individual victim via a crafted link.

The Stored Attack β€” Everyone Gets It

The second type, Priya explains, is persistent XSS β€” also called stored XSS. "Same idea, but far more dangerous reach. Instead of sending a crafted link to one victim, the attacker posts their script directly onto a site that stores user content. A forum, a social media platform, a comment section. The script is now part of the site's own database. Every user who loads that page gets the script served to them automatically β€” no crafted link required."

She walks through the social media scenario. An attacker posts a comment on a public forum. The comment looks normal but contains embedded JavaScript. Every user who reads the comment has the script execute in their browser silently. On social networks, the script can go further β€” it can be written to automatically re-post the comment to the viewer's own profile. Now every person who follows the viewer sees it, and the script propagates to their browsers too. "In the right environment," Priya says, "a single stored XSS post can spread like a virus β€” self-replicating across the social graph. We've seen this in the real world. Thousands of accounts compromised in hours from one malicious post."

πŸ’‘ Stored XSS Persistent (stored) XSS saves the malicious script on the server β€” in a database, comment field, or forum post. Every user who views the infected page has the script execute in their browser automatically, without clicking a crafted link. On social platforms, scripts can self-propagate across the network. Stored XSS has no specific individual target β€” it attacks every viewer of the infected page.

The Subaru Hack β€” One Token to Rule Them All

Priya pulls up a case study from June 2017. Security researcher Aaron Guzman was examining Subaru's online vehicle management portal. When a user logged in, they received an authentication token that allowed them to manage their vehicle remotely. The first problem: this token never expired. Unlike well-designed authentication tokens that expire after hours or days and require re-authentication, Subaru's token was permanent. Whoever had it, had access forever.

The second problem: the token was overpowered. It didn't just allow you to manage your own vehicle β€” it allowed any service request the portal supported, including adding an email address to someone else's account. Once your email was on another user's account, the token granted you full access to their vehicle management too. The third problem β€” where XSS connected everything β€” was that the web front-end had an XSS vulnerability. An attacker could send a malicious link to a Subaru owner. If the owner clicked it, the XSS exploit ran in their browser, captured the authentication token, and sent it to the attacker. With that one token β€” permanent, omnipotent β€” the attacker could access any vehicle whose owner they'd targeted, forever. "The XSS wasn't the whole attack," Priya tells the team. "But it was the delivery mechanism. The token was the prize. XSS was the hand that reached in and took it."

πŸ’‘ XSS as a Delivery Mechanism In the Subaru 2017 case, XSS was used to steal a never-expiring authentication token that gave permanent access to vehicle management β€” including other users' vehicles. This illustrates how XSS is often a component in a larger attack chain rather than a standalone exploit. XSS captures credentials, tokens, or session data; those assets then enable further access.

Stopping the Script

The team asks how to defend against XSS. Priya gives them two perspectives: developer and user. "From the developer side, the most important control is input validation and output encoding. You never let user-submitted content be rendered as HTML or executed as script. Every piece of user input is treated as data β€” plain text β€” and encoded before it's sent back to the browser. The browser then displays it as text, not executes it as code." She adds: same principle as SQL injection. The vulnerability is the same root cause β€” treating untrusted input as code.

"For users: don't click links you don't fully trust, especially in email. If you're going to a website, type the address yourself or use a bookmark. Consider a browser extension that controls which domains can run JavaScript. And keep your browser updated β€” vendors patch XSS-related vulnerabilities regularly." She closes with the developer emphasis: "The user mitigations reduce exposure but don't fix the vulnerability. The fix lives in the code. If the application properly encodes output and validates input, it doesn't matter what the user does β€” their browser will never execute an attacker's script through your site."

βœ… XSS Defense Stack For developers (primary): Input validation β€” reject or sanitize script content in user input; Output encoding β€” encode user content before rendering so browsers display it as text, not code; Content Security Policy (CSP) β€” restrict which scripts the page is allowed to execute. For users: Don't click untrusted links; Use browser extensions to control JavaScript execution; Keep browsers updated to patch known vulnerabilities.