Last modified: April 21, 2025
XSS
Cross Site Scripting Attacks
- vulnerability that allows attackers to inject malicious scripts into web pages viewed by other users
import * as cheerio from 'cheerio';
import express from 'express';
const app = express();
const escapeHTML = str => String(str).replace(/[&<>'"]/g,
tag => ({
'&': '&',
'<': '<',
'>': '>',
"'": ''',
'"': '"'
}[tag]));
// This string mocks what a user might *try* to input on the client side of your site...
// you should not allow this/clean it up
const userInputWithHTML = "<span id=userInput>Don't allow <em>tags</em> to be <strong>rendered</strong></span>"
// This function mocks the unsafe way of doing this: it doesn't clean the string
// or check if anything vulnerable is in here. When you see it rendered, you'll notice
// that user inputted <em> and <strong> remain
function vulnerableAddUserInput(){
return `
<p>
<strong>Here is the user input:</strong> ${userInputWithHTML}
</p>
`
}
// use an excapeHTML function to clean the user input first
function fixWithFunction () {
return `
<p>
<strong>Here is the user input: </strong> ${escapeHTML(userInputWithHTML)}
</p>
`
}
function fixWithInnerText() {
let htmlString = `
<p>
<strong>Here is the user input: </strong> <span id='userInput1'></span>
`
const parsedHTML = cheerio.load(htmlString)
parsedHTML("#userInput1").text(userInputWithHTML)
return parsedHTML.html()
}
// Below are the sections we'll be working though to learn how to avoid XSS
app.get('/', (req, res) => {
res.send(`
<html><body>
<h1> demo for xss escaping</h1>
<h2> vulnerable user input </h2>
${vulnerableAddUserInput()}
<h2> fix with function </h2>
${fixWithFunction()}
<h2> fix with inner HTML </h2>
${fixWithInnerText()}
</body>
</html>
`)
})
app.listen(3000, ()=>{
console.log('Example app listening at http://localhost:3000')
})