Cross-Site Scripting (XSS) vulnerabilities remain one of the most common and dangerous flaws in web applications today. Yet many developers don’t get hands-on experience with how they work—until it’s too late. In this article, we’ll build a small, local XSS payload tester using Node.js. The goal is to help you understand how XSS works, test your own input fields, and build safer apps as a result.
Let’s dive in.
🧠 What You’ll Learn
- What XSS is and how it works
- How to build a simple Node.js app that reflects user input
- How to test various XSS payloads in a safe environment
- Basic techniques to defend against XSS
⚙️ What You’ll Need
- Node.js installed on your machine
- Basic knowledge of JavaScript and Express.js
- A modern browser (Chrome, Firefox, etc.)
📦 Step 1: Set Up Your Project
Let’s start with a basic Express server.
mkdir xss-tester
cd xss-tester
npm init -y
npm install express
Now, create a file called app.js
:
const express = require('express');
const app = express();
const port = 3000;
// Enable parsing of POST data
app.use(express.urlencoded({ extended: true }));
app.get('/', (req, res) => {
const input = req.query.input || '';
res.send(`
<h1>XSS Tester</h1>
<form method="GET" action="/">
<input name="input" placeholder="Enter some text" />
<button type="submit">Submit</button>
</form>
<p>Output:</p>
<div>${input}</div>
`);
});
app.listen(port, () => {
console.log(`XSS tester running at http://localhost:${port}`);
});
Run it using:
node app.js
Then visit: http://localhost:3000
This simple app takes user input from the query string and reflects it back in the response. That’s all it takes to create a basic XSS lab.
⚠️ Step 2: Try Out XSS Payloads
Now that your local tester is running, let’s explore how XSS works. Try entering this into the input field:
<script>alert('XSS')</script>
These work because the application is directly injecting user input into the DOM without sanitization or encoding. In a real-world app, that could lead to session theft, defacement, or more dangerous outcomes.
🛡️ Step 3: Preventing XSS
Now that we’ve seen how easy it is to create a vulnerable app, let’s talk about fixes.
✅ Basic Fix: Escape User Input
Modify the line where user input is rendered:
const escapeHtml = (unsafe) =>
unsafe
.replace(/&/g, '&')
.replace(/</g, '<')
.replace(/>/g, '>')
.replace(/"/g, '"')
.replace(/'/g, ''');
res.send(`
<h1>XSS Tester</h1>
<form method="GET" action="/">
<input name="input" placeholder="Enter some text" />
<button type="submit">Submit</button>
</form>
<p>Output:</p>
<div>${escapeHtml(input)}</div>
`);
Now when you enter <script>alert('XSS')</script>
, it shows up as plain text—no alert, no script execution.
🔐 More Security Tips:
- Use templating engines (like Pug or Handlebars) which auto-escape by default.
- Sanitize input using libraries like DOMPurify (on the frontend) or validator libraries on the backend.
- Apply Content Security Policy (CSP) headers to limit what scripts are allowed to run.
💡 Why Build a Tester Like This?
Many devs read about XSS but never actually see it in action. A simple tool like this helps you:
- Test real payloads safely, without risking your main app
- Understand how different contexts (HTML, JavaScript, attributes) behave differently
- Validate that your security defenses actually work
You can even modify this script to test POST requests or simulate user cookies, making it a great educational sandbox.
🧪 Bonus: Add a POST Version
If you want to test XSS through POST data (not just URL params), update your app like this:
app.get('/post', (req, res) => {
res.send(`
<h1>XSS Tester (POST)</h1>
<form method="POST" action="/post">
<input name="input" placeholder="Enter some text" />
<button type="submit">Submit</button>
</form>
`);
});
app.post('/post', (req, res) => {
const input = req.body.input || '';
res.send(`
<h1>XSS Tester (POST)</h1>
<form method="POST" action="/post">
<input name="input" placeholder="Enter some text" />
<button type="submit">Submit</button>
</form>
<p>Output:</p>
<div>${input}</div>
`);
});
Try visiting /post
and submitting payloads there too.
🧵 Wrapping Up
Cross-Site Scripting is dangerous precisely because it’s so easy to introduce. Hopefully, building this local XSS payload tester helped you see how XSS works in real time—and more importantly, how to stop it.
As developers, we’re often the first (and last) line of defense. Understanding how vulnerabilities happen isn’t just for security experts—it’s part of writing good code.
Try tweaking the app. Add pages, test escaping logic, or plug it into a template engine. The more you experiment, the more you'll learn.
Top comments (0)