Cross-Site Scripting (XSS)

A web vulnerability where attackers inject malicious scripts into web applications, enabling theft of user data or unauthorized actions.

Cross-Site Scripting (XSS) is one of the most prevalent and dangerous web security vulnerabilities, ranked in the OWASP Top 10. In the context of Web3 and DeFi applications, XSS attacks are particularly devastating—they can manipulate transaction data before signing, steal wallet credentials, or trick users into approving malicious transactions. While smart contract audits focus on on-chain code, XSS vulnerabilities in dApp frontends can completely bypass secure contracts.

How XSS Works

XSS occurs when an application includes untrusted data in a web page without proper validation or escaping. The browser executes the injected script with full access to the page context:

1// Vulnerable code: directly inserting user input into HTML
2const username = getUserInput(); // User enters: <script>stealWallet()</script>
3document.getElementById('welcome').innerHTML = `Welcome, ${username}`;
4// Result: The malicious script executes!

Types of XSS

Reflected XSS

The malicious script comes from the current HTTP request:

1https://dapp.example.com/search?q=<script>stealCookies()</script>

The server includes the search query in the response without sanitization, and the script executes when the victim clicks the crafted link.

Stored XSS

The malicious script is permanently stored on the target server:

1// Attacker submits profile bio containing:
2// <img src=x onerror="sendWalletToAttacker()">
3
4// When other users view the profile, the script executes

More dangerous than reflected XSS because it affects all users who view the stored content.

DOM-Based XSS

The vulnerability exists entirely in client-side code:

1// Vulnerable: Using URL fragment directly
2const tokenAddress = window.location.hash.substring(1);
3document.write(`Loading token: ${tokenAddress}`);
4
5// Attacker crafts: https://dapp.com/#<script>drainWallet()</script>

XSS in Web3: Why It's Critical

In traditional web apps, XSS might steal session cookies. In Web3 applications, the stakes are higher:

Transaction manipulation: Scripts can modify transaction parameters before signing:

1// Attacker's injected script
2const originalSend = wallet.sendTransaction;
3wallet.sendTransaction = async (tx) => {
4 tx.to = "0xATTACKER_ADDRESS"; // Redirect funds
5 return originalSend(tx);
6};

Approval hijacking: Trick users into signing unlimited token approvals:

1// Injected script requests MAX approval to attacker's contract
2await token.approve(attackerContract, ethers.MaxUint256);

Wallet draining: Execute transactions directly using connected wallets:

1// If wallet is connected, attacker can initiate transfers
2const signer = await provider.getSigner();
3await signer.sendTransaction({
4 to: attackerAddress,
5 value: ethers.parseEther("10")
6});

Real-World Web3 XSS Examples

NFT metadata attacks: Malicious scripts embedded in NFT metadata that execute when viewed on marketplaces.

DeFi dashboard exploits: XSS in portfolio trackers that could manipulate displayed balances or inject malicious transaction requests.

Bridge interface attacks: Scripts modifying destination chains or addresses in cross-chain bridge UIs.

Prevention Techniques

Output Encoding

Encode data based on context:

1// HTML context: escape HTML entities
2function escapeHtml(str) {
3 return str.replace(/[&<>"']/g, char => ({
4 '&': '&amp;',
5 '<': '&lt;',
6 '>': '&gt;',
7 '"': '&quot;',
8 "'": '&#039;'
9 }[char]));
10}
11
12document.getElementById('welcome').innerHTML =
13 `Welcome, ${escapeHtml(username)}`;

Content Security Policy (CSP)

HTTP headers that restrict script sources:

1Content-Security-Policy: default-src 'self'; script-src 'self' 'sha256-abc123...'

This prevents inline scripts and restricts external script sources.

Input Validation

Validate and sanitize all user inputs:

1import DOMPurify from 'dompurify';
2
3// Sanitize HTML content
4const cleanHtml = DOMPurify.sanitize(userInput);
5
6// Validate addresses
7function isValidAddress(addr: string): boolean {
8 return /^0x[a-fA-F0-9]{40}$/.test(addr);
9}

Use Framework Protections

Modern frameworks provide automatic escaping:

1// React automatically escapes values in JSX
2function Welcome({ username }) {
3 return <div>Welcome, {username}</div>; // Safe!
4}
5
6// DANGER: dangerouslySetInnerHTML bypasses protection
7<div dangerouslySetInnerHTML={{ __html: userInput }} /> // Vulnerable!

XSS Testing in Audits

TypeScript audits should include:

  1. Identify injection points: All places where user input is rendered
  2. Test encoding: Verify proper context-aware encoding
  3. Check CSP: Review Content Security Policy headers
  4. Test DOM manipulation: Look for unsafe innerHTML, document.write, eval
  5. Review third-party integrations: Libraries rendering user content

Audit Checklist

  • All user inputs sanitized before rendering
  • Content Security Policy implemented
  • No use of innerHTML with untrusted data
  • No eval() or new Function() with user data
  • Framework security features enabled (not bypassed)
  • Third-party widgets/embeds sandboxed
  • Wallet interactions protected from script injection

XSS remains a critical vulnerability in Web3 applications. While smart contracts may be secure, a single XSS vulnerability can enable attackers to manipulate transactions, steal funds, or drain connected wallets—making frontend security audits essential for comprehensive dApp protection.

Need expert guidance on Cross-Site Scripting (XSS)?

Our team at Zealynx has deep expertise in blockchain security and DeFi protocols. Whether you need an audit or consultation, we're here to help.

Get a Quote

oog
zealynx

Subscribe to Our Newsletter

Stay updated with our latest security insights and blog posts

© 2024 Zealynx