In this lesson, you will learn how to prevent Cross-Site Scripting (XSS) in Server-Side Rendering (SSR) environments by ensuring dynamic content is properly sanitized before it reaches the browser.
The SSR Security Challenge
When you use Node.js to render HTML on the server, you are essentially building a dynamic template that gets injected directly into the user’s DOM. The danger arises when untrusted data—such as user profiles, comments, or custom input—is concatenated directly into these templates. If an attacker submits a script tag like <script>alert('xss')</script>, and your server renders it as raw HTML, the browser will execute that malicious code the moment the page loads.
Unlike Client-Side Rendering, where frameworks like React or Vue automatically escape content by default, manual string-based SSR (often found in legacy Express apps or custom template engines) is highly susceptible to injection if you aren't careful.
Sanitizing Before Rendering
To mitigate this, you must treat all data as "tainted" until explicitly sanitized. The goal is to strip or encode dangerous characters (like <, >, ", ') so the browser treats the input as literal text rather than executable markup.
Consider this unsafe example:
// Unsafe: Directly injecting user content
const template = `<div>Hello, ${user.displayName}</div>`;
res.send(template);
If user.displayName contains HTML, your application is compromised. Instead, you must pass the input through a robust library (like dompurify with jsdom) before rendering:
const createDOMPurify = require('dompurify');
const { JSDOM } = require('jsdom');
const window = new JSDOM('').window;
const DOMPurify = createDOMPurify(window);
// Safe: Sanitizing content before template injection
const cleanName = DOMPurify.sanitize(user.displayName);
const template = `<div>Hello, ${cleanName}</div>`;
res.send(template);
By sanitizing the content on the server before the HTML string is constructed, you ensure that even if malicious code is present in your database, it is neutralized before it ever reaches the client’s browser. Never trust data just because it originated from your own database; always validate and sanitize content at the point of template injection.
Key Takeaway: Never inject unsanitized dynamic data directly into SSR templates; always sanitize content before rendering to ensure the browser interprets it as plain text rather than active script.