You’re in a technical interview. The interviewer leans back and asks:
“How would you protect a web app from Cross-Site Scripting (XSS)?”
For a moment, your mind races. You know XSS is common, but how do you explain it clearly under pressure?
Let’s break it down with a simple example: imagine a user comment box on a blog. Instead of leaving normal feedback, an attacker types:
<script>alert('hacked')</script>
If the site displays that comment without filtering or escaping it, the attacker’s code will run in the browser of anyone who visits the page. What should have been a friendly comment section just became a tool for stealing sessions, hijacking accounts, or tricking users into clicking malicious links.
That’s Cross-Site Scripting in a nutshell: when untrusted input makes its way into a webpage, and browsers treat it as executable code instead of harmless text.
Why does this matter? Because XSS is still listed in the OWASP Top 10 — which means that even after two decades, it continues to affect real-world applications. From social media platforms to e-commerce sites, XSS has caused data leaks, account takeovers, and reputational damage for companies that overlooked secure coding practices.
What is XSS? (Explained Simply)
Think of XSS like this: imagine a suggestion box at a store. Customers drop in notes with feedback. Most are normal, like “Great service!” But one prankster slips in a fake note that says “Read this out loud and embarrass yourself.” The staff reads it, not realizing it’s a trick.
That’s what Cross-Site Scripting does — only instead of words, the “fake note” is malicious code, and the victim is every user’s browser.
A Plain-English Example
Imagine a blog with a comment section. A normal user writes:
Great article, thanks for sharing!
But an attacker posts: <script>alert('hacked')</script>
If the site displays that input without escaping it, the browser doesn’t see it as a comment. It sees it as code to run. The result? Every visitor who loads that page gets an unexpected popup — or worse, the attacker could steal cookies, hijack accounts, or inject fake login forms.
The Flow of an XSS Attack
Here’s the basic lifecycle of how XSS sneaks in:
Attacker Input → Saved in Database → Rendered in HTML → Browser Executes Script
At first glance, it looks like harmless text. But once it reaches the browser, the line between “data” and “code” becomes indistinguishable.
Types of XSS (Quick Overview)
There are three common flavors of XSS:
- Stored XSS → malicious input is saved in the database (e.g., a comment or profile field).
- Reflected XSS → the malicious input comes from a URL or form, and the server reflects it back in the response.
- DOM-based XSS → the attack happens entirely in the browser when client-side JavaScript inserts untrusted input into the page.
Don’t worry if these terms feel new — we’ll use simple code examples in the next section to make them concrete.
XSS in Practice (Bad Examples)
So far we’ve talked about XSS in theory — now let’s see what it looks like in real code.
Imagine we’re building a simple comment feature for a website. A user types something, we save it, and then we display it back on the page. Straightforward, right? But if we’re not careful, this is exactly where XSS slips in.
❌ Go Example (Vulnerable Template Rendering)
In Go, if we use the text/template
package or print raw input without escaping, we risk injecting user input directly into the HTML:
package main
import (
"fmt"
"net/http"
)
func handler(w http.ResponseWriter, r *http.Request) {
comment := r.URL.Query().Get("comment")
// ❌ Vulnerable: directly writing unescaped input to the response
fmt.Fprintf(w, "<h1>User Comment:</h1> %s", comment)
}
func main() {
http.HandleFunc("/", handler)
http.ListenAndServe(":8080", nil)
}
Now if an attacker visits:
http://localhost:8080/?comment=<script>alert('XSS')</script>
Instead of displaying harmless text, the browser executes the <script>
tag, and everyone sees an alert box pop up.
❌ Node.js Example (Express App Rendering Input Directly)
The same issue happens in Node.js if we inject user input straight into the HTML without escaping:
const express = require('express');
const app = express();
app.get('/', (req, res) => {
const comment = req.query.comment;
// ❌ Vulnerable: directly embedding user input in HTML
res.send(`<h1>User Comment:</h1> ${comment}`);
});
app.listen(3000, () => console.log('Server running on http://localhost:3000'));
If someone visits:
http://localhost:3000/?comment=<script>alert('XSS')</script>
Boom — the browser pops up the alert box again. What should have been “just text” was treated as code.
The Key Takeaway: This is a simple example of how XSS works in practice. As we saw earlier, it can appear as stored, reflected, or DOM-based attacks — the form changes, but the root cause is always the same: untrusted input being injected into a page without proper handling.
How to Prevent XSS (Best Practices)
Now that we’ve seen how XSS sneaks into your program, let’s focus on defense. The good news is: protecting against XSS doesn’t require magic — just discipline and the right tools.
Think of prevention as layers of defense: some fixes are essential, others add extra safety nets. Together, they make your app resilient.
1. Output Encoding / Escaping
The golden rule: never trust user input when rendering it back to a page. Instead, escape it so the browser treats it as text, not code.
🚨 Vulnerable vs. ✅ Safe (Quick Comparison)
Sometimes, the fix is just one small change — but it makes all the difference.
In Golang
// ❌ Vulnerable: text/template does NOT escape input
import "text/template"
// ✅ Safe: html/template auto-escapes <script> and other dangerous tags
import "html/template"
In Node.js → if you use template engines like EJS, Pug, or Handlebars, they escape output by default.
// ❌ Vulnerable: raw string interpolation, user input becomes HTML
res.send(`<h1>User Comment:</h1> ${comment}`);
// ✅ Safe: use a template engine (EJS, Pug, Handlebars)
// Escaping happens by default
res.render('index', { comment });
Interview Tip: Be ready to explain that secure defaults (like Go’s html/template or Express template engines) are your best defense. Interviewers love to hear that you understand both the why and the how.
2. Use Security Libraries
Sometimes you do want to allow limited HTML (like bold or italic tags in a blog comment). In that case, use a sanitization library that strips dangerous tags and attributes.
Node.js → DOMPurify is a battle-tested option (often used with server-side rendering).
Go → packages like bluemonday provide configurable sanitizers.
3. Content Security Policy (CSP)
CSP is like a seatbelt: it won’t prevent an accident, but it can reduce the damage. By setting a CSP header, you can tell browsers to only run scripts from trusted sources.
Example header:
Content-Security-Policy: script-src 'self'
This means: only run JavaScript that comes from my own site, not from injected code.
4. Input Validation (Secondary Layer)
Validation alone won’t stop XSS (attackers can always craft clever payloads), but it can reduce noise. For example:
Reject inputs that are clearly invalid for the field (like a birthdate containing <script>
).
Limit length of input to reduce payload size.
Think of this as cleaning the water before it reaches your filters.
5. Principle of Least Privilege
Finally, even if something slips through, you can reduce the impact by limiting what the attacker can do.
Cookies → mark them HttpOnly and Secure, so even if a script runs, it can’t steal them easily.
Database / API access → don’t give your web app more permissions than it needs.
Key Takeaway:
If you remember only one thing:
Always escape untrusted output.
Everything else — sanitization, CSP, validation, least privilege — are extra shields. But escaping is your first and strongest line of defense.
Why Interviewers Ask About XSS
At this point, you might be wondering: why does XSS keep showing up in interviews? It’s not because interviewers enjoy watching developers sweat — it’s because this single vulnerability reveals a lot about how you think as an engineer.
Here’s what they’re really looking for:
1. Do you know client-side vulnerabilities, not just backend ones?
Many developers focus only on the server. But XSS is a reminder that security isn’t just about the database — the browser is part of the attack surface too.
2. Can you explain attack → defense clearly?
It’s one thing to say “XSS is bad.” It’s another to walk through:
- how it works (untrusted input becomes code), and
- how to fix it (escape output, sanitize input, use CSP). That step-by-step clarity is exactly what interviewers want to hear.
3. How do you handle pressure?
Sometimes the question isn’t asked in a calm, textbook way. Instead, you’ll hear things like:
- “What’s the difference between stored and reflected XSS?”
- “How would you prevent XSS in your favorite language?”
- “Can you give me a real-world consequence of XSS?”
These versions test whether you can stay calm, think out loud, and structure your answer under pressure.
4. Do you understand the real-world impact?
Interviewers want to know if you see beyond code snippets. For example, XSS can:
- Steal a user’s session cookies.
- Hijack an account.
- Inject fake login forms to harvest passwords.
- Damage a company’s reputation when users lose trust.
A Simple Answer Framework
Here’s a 3-step structure you can use to answer almost any XSS interview question:
1. Define it simply
> “XSS happens when untrusted input is inserted into a webpage and executed as code in the browser.”
2. Give a quick example
> “For instance, if a site displays alert('hacked') from a comment box without escaping, that code will run for every visitor.”
3. Show how to prevent it
> “The fix is to always escape output (Go’s html/template, Node’s template engines), sanitize inputs, and enforce a Content Security Policy as backup.”
The key takeaway: XSS questions aren’t trivia. They’re a way for interviewers to test your security fundamentals, your ability to reason through problems, and your communication skills under pressure.
Pop Quiz: Test Your Understanding
Before we wrap up, let’s do a quick self-check. These aren’t trick questions — just simple ways to reinforce what you’ve learned. Try answering them in the comments to share your take and help others learn too.
1. Scenario Check
Imagine a blog comment box that doesn’t escape <script>
tags. What happens if an attacker submits this as a comment?
<script>alert('hacked')</script>
2. True or False
Input validation alone is enough to completely stop XSS attacks.
3. Fix the Code (Go)
Here’s a vulnerable Go snippet:
fmt.Fprintf(w, "<p>%s</p>", comment)
Rewrite it using a safe approach so that <script>
tags are not executed in the browser.
4. Fix the Code (Node.js)
Here’s a similar vulnerable snippet in Node.js (Express):
res.send(`<p>${comment}</p>`);
Rewrite it using a templating engine or escaping function to prevent XSS.
💡 Tip: Don’t worry about being “perfect.” The goal is to practice identifying vulnerable code and thinking of safer alternatives.
Conclusion / Call to Action
Cross-Site Scripting (XSS) is dangerous because it hijacks user trust — but it’s also preventable with a few simple practices. If you remember one rule, let it be this: never trust unescaped user input in your HTML output. Add layers like sanitization, CSP, and cookie protections, and you’ll be ahead of most real-world vulnerabilities.
Key Takeaways
- XSS happens when untrusted input is executed as code in the browser.
- Output escaping (Go html/template, Node templating engines) is your first line of defense.
- Sanitization libraries (like DOMPurify) help when HTML input is unavoidable.
- Content Security Policy (CSP) and secure cookie flags (HttpOnly, Secure) provide safety nets.
Your Next Steps
- Audit your code for any direct string interpolation into HTML.
- Use safe defaults: html/template in Go, EJS/Pug/Handlebars in Node.
- Add or tighten a CSP header for your app.
- Set cookies with HttpOnly and Secure to limit exposure.
Over to You
Have you ever discovered an XSS issue in your own project — maybe even by accident?
What fixes or defensive tricks have you found most useful? Drop your answers and quiz responses in the comments — let’s learn from each other.
Up Next in the Series
This was Full-Stack Interview Prep #2: XSS Explained Simply (with Go & Node.js Examples), a follow up to #1: SQL Injection Explained Simply.
Stay tuned for #3: CSRF (Cross-Site Request Forgery) Explained Simply with Go & Node.js Examples.
Top comments (0)