Understanding Cross-Site Scripting - A Security Primer

Co-authored by: Nirmala NB | Website

Most engineers think they understand XSS. They don’t. You’ll hear people say “cross-sight,” but it’s actually “cross-site scripting.” We call it XSS because CSS was already taken by Cascading Style Sheets. If you work in web security or development, this is baseline knowledge you can’t skip.

The Core Mechanism

Here’s what happens: attackers inject malicious code into your web application, usually JavaScript. Your browser executes it because it can’t tell the difference between your legitimate scripts and their garbage. The reason? Your application doesn’t sanitize user input before spitting it back out.

graph LR
    A[Attacker] -->|Injects script| B[Web Application]
    B -->|Returns unsanitized| C[Victim Browser]
    C -->|Executes code| D[Attack Success]
    D -->|Steals data| A

Three Primary Categories

1. Persistent XSS (Stored)

The attacker’s payload lives in your database. Every user who hits that page gets infected. A forum post with malicious code? Everyone who reads it triggers the exploit. Attackers love this method for stealing credentials, logging keystrokes, or spreading malware fast.

sequenceDiagram
    participant Attacker
    participant Server
    participant Database
    participant Victim1
    participant Victim2

    Attacker->>Server: Submit malicious comment
    Server->>Database: Store payload
    Note over Database: Malicious script stored

    Victim1->>Server: Request page
    Server->>Database: Retrieve comments
    Database->>Server: Return with payload
    Server->>Victim1: Send page with script
    Note over Victim1: Script executes

    Victim2->>Server: Request same page
    Server->>Database: Retrieve comments
    Database->>Server: Return with payload
    Server->>Victim2: Send page with script
    Note over Victim2: Script executes

2. Non-Persistent XSS (Reflected)

The malicious content bounces right back from the server without getting stored. You see this in search queries or error messages. It’s temporary, but it works when you pair it with social engineering. Attackers craft sketchy URLs and trick people into clicking them.

sequenceDiagram
    participant Attacker
    participant Victim
    participant Server

    Attacker->>Victim: Send malicious link
    Note over Attacker and Victim: example.com/search?q=%3Cscript%3Ealert(1)%3C%2Fscript%3E
    Victim->>Server: Click link (send request)
    Server->>Victim: Reflect input in response
    Note over Victim: Browser executes script
    Victim->>Attacker: Session data leaked

3. Client-Side XSS (DOM-based)

This one never touches your server. JavaScript grabs data from the URL and shoves it straight into the page using something like innerHTML. No server processing means these bugs slip past a lot of security reviews.

graph TD
    A[User visits page] --> B[JavaScript reads URL]
    B --> C{Safe method?}
    C -->|innerHTML| D[Code executes - XSS]
    C -->|textContent| E[Safe rendering]

Defense Strategies

Tools like SuperTokens give you practical protection through HTTP-only cookie flags. JavaScript can’t touch session tokens. Their setup uses short-lived access tokens with longer refresh tokens, so you’re not exposing credentials for long. The server handles all auth decisions, and they bake in anti-CSRF protection.

graph TB
    A[Input Validation] --> B[Output Encoding]
    B --> C[Content Security Policy]
    C --> D[HTTP-Only Cookies]
    D --> E[Secure Session Management]

    F[XSS Attempt] --> A
    A -.blocks.-> G[Invalid Input]
    B -.sanitizes.-> H[Safe Output]
    C -.restricts.-> I[Script Blocked]
    D -.protects.-> J[Cookie Protected]
    E -.validates.-> K[Session Safe]

What Actually Works

Encode your output. Treat all user data as plain text, not executable code. Use textContent instead of innerHTML. Input validation helps, but don’t bet everything on it. Set up Content Security Policies to block unauthorized scripts.

React, Angular, and Vue give you automatic escaping, which cuts your risk way down. But you can still screw it up if you’re careless.

Dangerous Functions to Avoid

graph TD
    A[Dangerous Functions] --> B[eval]
    A --> C[innerHTML]
    A --> D[document.write]
    A --> E[setTimeout with string]

    B --> B1[Executes arbitrary code]
    C --> C1[Can execute scripts]
    D --> D1[Overwrites content]
    E --> E1[Acts like eval]

Session Theft Techniques

Session cookies are what attackers want. If you don’t set HTTP-only flags, scripts can read document.cookie. Basic attacks just grab that data and send it to the attacker’s server.

sequenceDiagram
    participant Victim Browser
    participant Malicious Script
    participant Attacker Server

    Note over Victim Browser: XSS vulnerability exploited
    Malicious Script->>Victim Browser: Read document.cookie
    Victim Browser->>Malicious Script: Return session token

    alt Noisy Method
        Malicious Script->>Victim Browser: window.location redirect
        Note over Victim Browser: Page flashes/reloads
        Victim Browser->>Attacker Server: Cookie in URL
    else Stealth Method
        Malicious Script->>Malicious Script: Create new Image()
        Malicious Script->>Attacker Server: Set img.src with cookie
        Note over Attacker Server: Cookie received silently
        Note over Victim Browser: No visible changes
    end

    Attacker Server->>Attacker Server: Store stolen session
    Note over Attacker Server: Impersonate victim
graph LR
    A1[JavaScript] -->|Without HTTP-Only| B1[Access Granted]
    B1 --> C1[Session Stolen]

    A2[JavaScript] -->|With HTTP-Only| B2[Access Denied]
    B2 --> C2[Session Protected]

Token-Based Security Architecture

graph TD
    A[User Logs In] --> B[Server Issues Tokens]
    B --> C[Short-lived Access Token]
    B --> D[Long-lived Refresh Token HTTP-Only]

    C --> E{Access Valid?}
    E -->|Yes| F[Grant Access]
    E -->|Expired| G[Use Refresh Token]

    G --> H{Refresh Valid?}
    H -->|Yes| I[New Access Token]
    H -->|No| J[Re-authenticate]

    I --> F

    K[XSS Attack] -.attempts.-> C
    K -.blocked.-> D

Comprehensive XSS Prevention Strategy

graph TB
    Start[User Input] --> Val[Validation]
    Val --> San[Sanitization]
    San --> Enc[Encoding]
    Enc --> Out[Output to Browser]

    Out --> CSP{CSP Active?}
    CSP -->|Yes| Safe1[Scripts Restricted]
    CSP -->|No| Risk1[Scripts Can Run]

    Out --> Cook{HTTP-Only?}
    Cook -->|Yes| Safe2[JS Cannot Access]
    Cook -->|No| Risk2[Vulnerable]

    Out --> Frame{Modern Framework?}
    Frame -->|Yes| Safe3[Auto-Escaping]
    Frame -->|No| Risk3[Manual Escaping]

    Safe1 --> Secure[Protected]
    Safe2 --> Secure
    Safe3 --> Secure

    Risk1 --> Vuln[Vulnerable]
    Risk2 --> Vuln
    Risk3 --> Vuln

Once attackers steal your credentials, they own the account. No password needed. That’s why you need multiple layers: HTTP-only cookies, solid session management, and enforced CSPs turn XSS from a critical threat into something you can actually manage.


Reference: MDN Web Security - XSS