XSS Attacks: Stored and Reflected

Serhii Koziy
4 min readJan 21, 2025

As a Senior Frontend Developer, one of the critical aspects of building secure applications is understanding and mitigating vulnerabilities like Cross-Site Scripting (XSS) attacks. This article will explore what XSS attacks are, focusing on stored and reflected XSS, and provide actionable strategies to secure your React and general frontend applications, with TypeScript examples where relevant.

What is an XSS Attack?

Cross-Site Scripting (XSS) is a type of security vulnerability that allows attackers to inject malicious scripts into web pages viewed by other users. These scripts typically run in the context of the victim’s browser, enabling attackers to steal cookies, session tokens, or sensitive data, and even perform actions on behalf of the user.

There are two primary types of XSS attacks:

  1. Stored XSS: The malicious script is permanently stored on the target server (e.g., in a database or file system). Victims unknowingly execute the script when they access the infected data.
  2. Reflected XSS: The malicious script is reflected off a web server, often via a query parameter, and is executed when a victim clicks a malicious link or visits a crafted URL.

Example of XSS Attacks

Stored XSS Example: Suppose a blog application allows users to submit comments without proper sanitization. An attacker could inject a script like this:

<script>alert('XSS Attack!');</script>

When another user views the comment, the script executes, displaying an alert or performing a more harmful action.

Reflected XSS Example: Consider a search page that displays the query parameter without escaping it:

const SearchPage = ({ query }: { query: string }) => {
return <div>Search results for: {query}</div>;
};

An attacker could craft a URL such as:

https://example.com/search?query=<script>alert('XSS');</script>

When the victim visits this URL, the script executes in their browser.

Protecting Your React Application

To secure your React application against XSS, you must follow best practices for sanitizing and validating user inputs and outputs. Let’s explore strategies to mitigate XSS attacks with practical examples in TypeScript.

1. Escape Output

Always escape any dynamic content rendered into the DOM to prevent malicious scripts from being executed.

React automatically escapes content rendered within JSX expressions:

const SafeOutput = ({ data }: { data: string }) => {
return <div>{data}</div>; // React escapes "data" automatically
};

However, avoid using it dangerouslySetInnerHTML unless absolutely necessary, and sanitize the input before rendering.

import DOMPurify from 'dompurify';

const SafeHTML = ({ html }: { html: string }) => {
const sanitizedHTML = DOMPurify.sanitize(html);
return <div dangerouslySetInnerHTML={{ __html: sanitizedHTML }} />;
};

2. Validate and Sanitize User Inputs

Always validate and sanitize user inputs on both the client and server side.

For example, using a TypeScript utility to validate input:

const isValidComment = (input: string): boolean => {
const forbiddenPatterns = /<script>|<img|onerror=|javascript:/i;
return !forbiddenPatterns.test(input);
};

const handleCommentSubmit = (comment: string) => {
if (!isValidComment(comment)) {
throw new Error('Invalid input detected!');
}
// Proceed with submitting sanitized comment
};

3. Use a Content Security Policy (CSP)

A CSP is a browser security feature that helps prevent XSS by restricting sources of content.

Add a CSP header to your server response:

Content-Security-Policy: script-src 'self';

This ensures that only scripts from the same origin are executed, blocking injected scripts from third-party sources.

4. Avoid Inline Scripts

Inline scripts can easily introduce XSS vulnerabilities. Ensure all scripts are loaded from external files.

5. Use Libraries and Frameworks That Prioritize Security

Rely on trusted libraries like DOMPurify for sanitizing HTML or xss for advanced sanitization needs.

import xss from 'xss';

const sanitizeInput = (input: string): string => {
return xss(input);
};

const SafeComponent = ({ content }: { content: string }) => {
const sanitizedContent = sanitizeInput(content);
return <div>{sanitizedContent}</div>;
};

6. Secure Query Parameters

Avoid directly rendering query parameters without validation. Use libraries like qs or query-string to parse and validate parameters safely.

import queryString from 'query-string';

const SearchPage = ({ location }: { location: string }) => {
const parsed = queryString.parse(location);
const query = String(parsed.query || '').replace(/</g, '&lt;').replace(/>/g, '&gt;');
return <div>Search results for: {query}</div>;
};

Conclusion

XSS attacks are a persistent threat to web applications, but you can effectively protect your React and general frontend applications by escaping outputs, validating inputs, using CSPs, and leveraging trusted libraries. Combining these practices with secure coding habits ensures a robust defense against both stored and reflected XSS vulnerabilities.

Remember, security is an ongoing process. Regularly audit your codebase, educate your team, and stay updated on the latest security practices to keep your applications safe.

Sign up to discover human stories that deepen your understanding of the world.

Free

Distraction-free reading. No ads.

Organize your knowledge with lists and highlights.

Tell your story. Find your audience.

Membership

Read member-only stories

Support writers you read most

Earn money for your writing

Listen to audio narrations

Read offline with the Medium app

No responses yet

Write a response