XSS protection
Basic Principles of XSS Attacks
XSS (Cross-Site Scripting) is a common web security vulnerability where attackers inject malicious scripts into web pages. When other users visit the page, these scripts execute in their browsers. XSS attacks are primarily divided into three types:
- Reflected XSS: Malicious scripts are sent to the server as part of a request, and the server "reflects" the script back in the response.
- Stored XSS: Malicious scripts are permanently stored on the target server (e.g., in a database).
- DOM-based XSS: The vulnerability exists in client-side code and does not involve server responses.
// A simple reflected XSS example
// Assume the URL is: http://example.com/search?query=<script>alert('XSS')</script>
app.get('/search', (req, res) => {
const query = req.query.query;
res.send(`<p>Search results: ${query}</p>`); // Dangerous! Directly outputs user input
});
XSS Protection Measures in Node.js
Input Validation and Filtering
Strict validation of all user input is the first line of defense against XSS. Validation should include:
- Data type checks
- Length restrictions
- Format validation (e.g., email, phone number)
- Special character filtering
// Using the validator library for input validation
const validator = require('validator');
function sanitizeInput(input) {
if (typeof input !== 'string') return '';
// Remove HTML tags
let clean = validator.stripLow(input);
clean = validator.escape(clean);
return clean;
}
app.post('/comment', (req, res) => {
const comment = sanitizeInput(req.body.comment);
// Store the processed comment
});
Output Encoding
When outputting data to an HTML page, proper encoding is essential:
- HTML entity encoding: Convert special characters to HTML entities.
- JavaScript encoding: Use when dynamically generating JavaScript code.
- URL encoding: Use for URL parameters.
// Using the he library for HTML entity encoding
const he = require('he');
function safeOutput(text) {
return he.encode(text, {
useNamedReferences: true,
strict: true
});
}
app.get('/profile', (req, res) => {
const username = req.query.username || 'Anonymous';
res.send(`<h1>Welcome, ${safeOutput(username)}</h1>`);
});
Using Secure Template Engines
Modern template engines often include built-in XSS protection:
- EJS: Escapes output by default.
- Pug/Jade: Automatically escapes output when using
=
. - Handlebars: Automatically escapes output when using
{{}}
.
// Example using the EJS template engine
const ejs = require('ejs');
app.set('view engine', 'ejs');
app.get('/product/:id', (req, res) => {
const product = {
id: req.params.id,
name: req.query.name || 'Default Product'
};
res.render('product', { product });
});
// product.ejs
<h2><%= product.name %></h2> <!-- Automatically escaped -->
Setting HTTP Security Headers
Enhance XSS protection by setting HTTP response headers:
// Using the helmet middleware to set security headers
const helmet = require('helmet');
app.use(helmet());
// Equivalent to:
app.use(helmet.xssFilter()); // Sets X-XSS-Protection
app.use(helmet.frameguard()); // Prevents clickjacking
app.use(helmet.noSniff()); // Prevents MIME type sniffing
app.use(helmet.hsts()); // HTTP Strict Transport Security
app.use(helmet.contentSecurityPolicy({
directives: {
defaultSrc: ["'self'"],
scriptSrc: ["'self'", "'unsafe-inline'"],
styleSrc: ["'self'", "'unsafe-inline'"],
imgSrc: ["'self'", "data:"]
}
}));
Implementing CSP (Content Security Policy)
Content Security Policy is one of the most effective modern web application defenses against XSS. It uses a whitelist mechanism to control which resources can be loaded and executed.
// Custom CSP policy
app.use((req, res, next) => {
res.setHeader(
'Content-Security-Policy',
"default-src 'self'; " +
"script-src 'self' https://trusted.cdn.com 'unsafe-inline'; " +
"style-src 'self' 'unsafe-inline'; " +
"img-src 'self' data:; " +
"font-src 'self'; " +
"connect-src 'self'; " +
"frame-src 'none'; " +
"object-src 'none'"
);
next();
});
XSS Protection for Rich Text Content
For user input that requires HTML formatting (e.g., comment systems), more granular filtering is needed:
// Using the xss library to process rich text
const xss = require('xss');
const myxss = new xss.FilterXSS({
whiteList: {
a: ['href', 'title', 'target'],
p: [],
br: [],
strong: [],
em: []
},
stripIgnoreTag: true,
onTagAttr: (tag, name, value) => {
// Only allow http/https links
if (tag === 'a' && name === 'href') {
if (/^https?:\/\//.test(value)) {
return name + '="' + xss.escapeAttrValue(value) + '"';
}
return '';
}
}
});
app.post('/rich-comment', (req, res) => {
const cleanHtml = myxss.process(req.body.content);
// Store the processed HTML
});
Cookie Security Settings
Prevent cookie theft via XSS attacks:
// Secure cookie settings
app.use(session({
secret: 'your-secret-key',
cookie: {
httpOnly: true, // Prevents JavaScript access
secure: true, // HTTPS only
sameSite: 'strict', // Prevents CSRF
maxAge: 24 * 60 * 60 * 1000
}
}));
Real-Time XSS Detection
Use tools during development to detect XSS vulnerabilities:
// Using the xss-filters library for real-time detection
const xssFilters = require('xss-filters');
app.post('/api/data', (req, res) => {
const userInput = req.body.input;
try {
// Throws an exception if XSS attempt is detected
xssFilters.inHTMLData(userInput);
// Safe processing
res.json({ status: 'success' });
} catch (err) {
// Log XSS attempt
console.warn('XSS attempt detected:', userInput);
res.status(400).json({ error: 'Invalid input' });
}
});
XSS Protection in Frontend Frameworks
Modern frontend frameworks like React, Vue, and Angular have built-in XSS protection mechanisms:
// React example - automatically escapes all variables
function UserProfile({ username }) {
// Safe, username is automatically escaped
return <div>Hello, {username}</div>;
}
// Dangerous! Use dangerouslySetInnerHTML with caution
function RichText({ content }) {
const cleanHtml = sanitize(content); // Must sanitize first
return <div dangerouslySetInnerHTML={{ __html: cleanHtml }} />;
}
Logging and Monitoring
Logging all potential XSS attack attempts is crucial for security audits:
// Using winston to log security events
const winston = require('winston');
const { Loggly } = require('winston-loggly-bulk');
const logger = winston.createLogger({
transports: [
new winston.transports.File({ filename: 'security.log' }),
new Loggly({
token: 'your-loggly-token',
subdomain: 'your-subdomain',
tags: ['security', 'xss'],
json: true
})
]
});
app.use((req, res, next) => {
// Check if the request contains suspicious scripts
if (JSON.stringify(req.body).includes('<script>')) {
logger.warn('Potential XSS attempt', {
url: req.url,
ip: req.ip,
payload: req.body
});
}
next();
});
Regular Security Audits
Regular security audits and code reviews are essential for maintaining application security:
- Use automated tools to scan for vulnerabilities (e.g., OWASP ZAP, Burp Suite).
- Conduct manual code reviews, especially focusing on all user input handling points.
- Keep dependencies updated and patch known vulnerabilities promptly.
- Perform penetration testing to simulate real attack scenarios.
// Use npm audit to check for dependency vulnerabilities
// package.json
{
"scripts": {
"audit": "npm audit --production",
"audit:fix": "npm audit fix --production"
}
}
本站部分内容来自互联网,一切版权均归源网站或源作者所有。
如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn