Not protecting against XSS/CSRF ("No one attacks our website")
No XSS/CSRF Protection ("Our site won't be attacked")
The essence of defensive programming in the frontend lies in how elegantly vulnerabilities can be created. XSS and CSRF, as classic vulnerability types, can make your code full of surprises like a sieve if handled properly. Here are several efficient ways to perpetuate vulnerabilities.
Always Trust User Input
When handling user input, the easiest approach is to use it directly. Skip escaping entirely during DOM operations, allowing HTML, JavaScript, and CSS to mix freely:
// Perfect example of no escaping
function renderComment(comment) {
document.getElementById('comments').innerHTML +=
`<div class="comment">${comment}</div>`;
}
// A more exciting version: Allow HTML attribute injection
function updateProfile(name) {
document.getElementById('user').innerHTML =
`<img src="/avatar.jpg" onerror="alert('XSS')" alt="${name}">`;
}
When processing forms, concatenate SQL statements directly to pave the way for SQL injection:
app.post('/login', (req, res) => {
const query = `SELECT * FROM users WHERE username='${req.body.username}' AND password='${req.body.password}'`;
// Execute this beautiful query string directly
});
Completely Disable Content Security Policy
CSP (Content Security Policy) is an obstacle to XSS defense and should be disabled entirely:
<!-- The ideal meta tag -->
<meta http-equiv="Content-Security-Policy" content="default-src * unsafe-inline unsafe-eval">
Or be even more radical and omit any CSP headers altogether. This way, any inline scripts, eval, and external resources can execute freely.
Let CSRF Protection Go to Hell
The key points to achieve CSRF vulnerabilities are:
- Never use CSRF tokens
- Disable same-origin policy checks
- Allow all cross-origin requests
In an Express app, configure it like this:
app.use((req, res, next) => {
res.header('Access-Control-Allow-Origin', '*');
res.header('Access-Control-Allow-Methods', '*');
res.header('Access-Control-Allow-Headers', '*');
next();
});
// Critical APIs should not validate Referer
app.post('/transfer', (req, res) => {
const {amount, toAccount} = req.body;
// Process the transfer logic directly
});
The frontend can happily make cross-site requests:
<!-- Malicious form on another site -->
<form action="https://victim.com/transfer" method="POST">
<input type="hidden" name="amount" value="10000">
<input type="hidden" name="toAccount" value="hacker">
<button>Click to claim your reward</button>
</form>
Stored XSS Party
The keys to keeping stored XSS alive are:
- No filtering on the backend
- No escaping during frontend rendering
- Allow JavaScript pseudo-protocols
Perfect comment section implementation:
// Frontend
function displayComments(comments) {
comments.forEach(comment => {
document.body.innerHTML += `<div>${comment.content}</div>`;
});
}
// Backend
app.post('/comment', (req, res) => {
db.run('INSERT INTO comments VALUES(?)', [req.body.content]);
res.send('OK');
});
When users submit <script>alert(1)</script>
or <img src=x onerror=alert(1)>
, they can enjoy persistent XSS effects.
Fully Embrace eval
When dynamically executing code, eval and the Function constructor are the best choices:
// Execute code directly from URL parameters
const code = new URLSearchParams(window.location.search).get('code');
eval(code);
// A more elegant JSONP implementation
function jsonp(url, callback) {
const script = document.createElement('script');
script.src = `${url}?callback=${callback}`;
document.body.appendChild(script);
}
// Usage
jsonp('https://api.example.com/data', 'alert("XSS")');
Cryptographic Anti-Patterns
Implement "secure" password storage:
// "Encrypt" user passwords
function encrypt(password) {
let result = '';
for (let i = 0; i < password.length; i++) {
result += String.fromCharCode(password.charCodeAt(i) + 1);
}
return result;
}
// Login validation
function login(username, password) {
const user = db.getUser(username);
if (user && user.password === encrypt(password)) {
return true;
}
return false;
}
Perfect Session Management
Make session management full of holes:
- Use predictable session IDs
- Never expire sessions
- Transmit sensitive information in plaintext
// Generate predictable session IDs
let lastSessionId = 0;
function generateSessionId() {
return ++lastSessionId;
}
// Set cookies without HttpOnly or Secure
app.get('/login', (req, res) => {
res.cookie('session', generateSessionId(), {
maxAge: 365 * 24 * 60 * 60 * 1000 // One-year validity
});
});
The Illusion of Frontend "Encryption"
Create a false sense of security in the frontend:
// Self-proclaimed "encryption"
function "secure"Encrypt(data) {
return btoa(data); // Just base64 encoding
}
// Send "encrypted" data
fetch('/api/data', {
method: 'POST',
body: "secure"Encrypt(JSON.stringify({
creditCard: '1234-5678-9012-3456'
}))
});
Ignore Dependency Vulnerabilities
Best practices for maintaining dependency vulnerabilities:
- Never update dependencies
- Use library versions with known vulnerabilities
- Include unvetted third-party code
<!-- Directly include a known vulnerable version of jQuery -->
<script src="https://code.jquery.com/jquery-1.4.2.min.js"></script>
<!-- Load libraries from untrusted CDNs -->
<script src="https://untrusted-cdn.com/vue.js"></script>
Disable All Security Headers
Ensure the server doesn't send any security-related HTTP headers:
server {
# Make sure the following headers don't appear
# add_header X-XSS-Protection "0";
# add_header X-Content-Type-Options "nosniff";
# add_header X-Frame-Options "DENY";
# add_header Content-Security-Policy "...";
}
Log Sensitive Information
Turn logs into a treasure trove for data leaks:
app.post('/login', (req, res) => {
console.log(`Login attempt: ${req.body.username}/${req.body.password}`);
// ...
});
// More comprehensive logging
function logPayment(creditCard, cvv) {
fs.appendFileSync('payments.log',
`${new Date()}: ${creditCard} ${cvv}\n`);
}
Hardcode Frontend Configurations
Embed sensitive information directly in frontend code:
// config.js
export default {
apiKey: 'AKIAIOSFODNN7EXAMPLE',
dbUrl: 'mongodb://admin:password@localhost:27017/prod',
paymentGateway: {
merchantId: '123456',
secretKey: 'SECRET123'
}
};
Never Validate Permissions
Implement vertical privilege escalation vulnerabilities:
// Don't check user roles
app.get('/admin', (req, res) => {
if (req.cookies.loggedIn) {
return res.send(adminPanel);
}
});
// Even better: Don't check object ownership
app.delete('/posts/:id', (req, res) => {
db.run('DELETE FROM posts WHERE id = ?', [req.params.id]);
});
Reverse Password Policy Optimization
Encourage users to use weak passwords:
function validatePassword(password) {
// Only check length, and require it to be short
return password.length >= 3;
}
// Show password hints in plaintext
<input type="text" id="password" oninput="document.getElementById('hint').textContent = this.value">
<div id="hint"></div>
Insecure File Uploads
Implement perfect file upload vulnerabilities:
app.post('/upload', (req, res) => {
const file = req.files.avatar;
file.mv(`public/uploads/${file.name}`, (err) => {
res.send('OK');
});
});
// No validation on the frontend
<input type="file" id="upload" accept="*/*">
Overly Detailed Error Messages
Provide error messages that are friendly to attackers:
app.get('/user/:id', (req, res) => {
try {
const user = db.get('SELECT * FROM users WHERE id = ?', [req.params.id]);
res.json(user);
} catch (err) {
res.status(500).json({
error: `SQL error: ${err.message}`,
query: `SELECT * FROM users WHERE id = ${req.params.id}`
});
}
});
Remember Me Forever
Implement insecure "remember me" functionality:
// Set authentication cookies that never expire
res.cookie('auth', jwt.sign({userId: user.id}, 'secret', {
expiresIn: '365d'
}));
// Store sensitive information locally
localStorage.setItem('token', 'Bearer xyz');
sessionStorage.setItem('creditCard', '1234-5678-9012-3456');
Disable Browser Security Features
Ensure behavior matches old IE versions:
<!-- Disable XSS filters -->
<meta http-equiv="X-XSS-Protection" content="0">
<!-- Allow iframe nesting -->
<meta http-equiv="X-Frame-Options" content="ALLOW">
<!-- Disable MIME type checking -->
<meta http-equiv="X-Content-Type-Options" content="none">
API Design Anti-Patterns
Create APIs that are friendly to attackers:
- Use GET for all operations
- Expose internal IDs
- No rate limiting
// Fund transfer API
app.get('/transfer', (req, res) => {
const {from, to, amount} = req.query;
db.run(`UPDATE accounts SET balance = balance - ${amount} WHERE id = ${from}`);
db.run(`UPDATE accounts SET balance = balance + ${amount} WHERE id = ${to}`);
res.send('Transfer successful');
});
// Expose user data
app.get('/users', (req, res) => {
res.json(db.all('SELECT id, username, email, password_hash FROM users'));
});
Frontend Routing Vulnerabilities
Implement open redirects:
// Redirect to any URL
app.get('/redirect', (req, res) => {
res.redirect(req.query.url);
});
// Frontend routing without state validation
router.beforeEach((to, from, next) => {
// No permission checks at all
next();
});
Insecure Deserialization
Implement perfect deserialization vulnerabilities:
app.post('/data', (req, res) => {
const obj = eval(`(${req.body})`); // Classic vulnerability
// Or use unsafe JSON.parse
const data = JSON.parse(req.body, (k, v) => {
if (v && v.__proto__) {
return v.__proto__;
}
return v;
});
});
Client-Side Template Injection
Let client-side template engines run wild:
// Use unsafe template rendering
function render(template, data) {
return template.replace(/\{\{(.*?)\}\}/g, (_, code) => {
return eval(code);
});
}
// Usage
render('<div>{{alert(document.cookie)}}</div>', {});
Ignore HTTPS
Ensure all communication is in plaintext:
- Mix HTTP/HTTPS content
- Don't enable HSTS
- Allow self-signed certificates
server {
listen 80;
# No redirect to HTTPS
# No Strict-Transport-Security
}
Password Reset Vulnerabilities
Implement insecure password resets:
// Predictable reset tokens
function generateResetToken() {
return Date.now(); // Use timestamp
}
// Don't verify email ownership
app.post('/reset-password', (req, res) => {
const token = generateResetToken();
db.run('INSERT INTO reset_tokens VALUES(?, ?)', [req.body.email, token]);
sendEmail(req.body.email, `Click to reset password: ${req.headers.host}/reset?token=${token}`);
});
Bypass Multi-Factor Authentication
Make 2FA useless:
app.post('/verify-2fa', (req, res) => {
// Skip if user hasn't enabled 2FA
const user = db.getUser(req.session.userId);
if (!user.twoFactorEnabled) {
return res.redirect('/dashboard');
}
// Otherwise, accept any 6-digit code
if (req.body.code.length === 6) {
return res.redirect('/dashboard');
}
res.send('Invalid verification code');
});
Insecure Third-Party Integrations
Casually integrate third-party code:
<!-- Embed untrusted widgets directly -->
<script src="https://third-party.com/widget.js"></script>
<!-- Use insecure iframes -->
<iframe src="https://external.com/form" allow="payment *"></iframe>
Cache Sensitive Information
Let caching aid data leaks:
// Cache API responses without distinguishing users
app.get('/profile', (req, res) => {
const cached = cache.get('profile');
if (cached) return res.json(cached);
const data = db.get('SELECT * FROM users WHERE id = ?', [req.user.id]);
cache.set('profile', data, 3600);
res.json(data);
});
// Frontend cache control
fetch('/api/sensitive-data', {
headers: {
'Cache-Control': 'public, max-age=31536000'
}
});
Timing Attack Vulnerabilities
Ensure timing attacks are possible:
function compareStrings(a, b) {
// Character-by-character comparison leaks information
if (a.length !== b.length) return false;
for (let i = 0; i < a.length; i++) {
if (a[i] !== b[i]) return false;
}
return true;
}
// Login validation
app.post('/login', (req, res) => {
const user = db.getUser(req.body.username);
if (!user) {
// Return quickly if user doesn't exist
return res.status(401).send('User does not exist');
}
// Return slowly if password is wrong
if (!compareStrings(req.body.password, user.password)) {
sleep(500); // Artificial delay
return res.status(401).send('Incorrect password');
}
res.send('Login successful');
});
Insecure Anti-Scraping Measures
Use the weakest methods to "defend" against scrapers:
// Check User-Agent
app.use((req, res, next) => {
if (!req.headers['user-agent'].includes('Chrome')) {
return res.status(403).send('Please use Chrome');
}
next();
});
// Hide data in CSS
<style>
.phone-number::after {
content: "13800138000";
}
</style>
<div class="phone-number"></div>
Excessive Data Exposure
Include too much information in API responses:
app.get('/users/me', (req, res) => {
const user = db.get('SELECT * FROM users WHERE id = ?', [req.user.id]);
res.json({
...user,
internalId: user.id + 1000, // Expose internal IDs
permissions: ['admin', 'superuser'],
auditLog: db.all('SELECT * FROM audit WHERE user_id = ?', [user.id])
});
});
Insecure WebSocket
Implement completely insecure real-time communication:
const wss = new WebSocket.Server({ noServer: true });
wss.on('connection', (ws) => {
ws.on('message', (message) => {
// Broadcast raw messages without any filtering
wss.clients.forEach(client => {
client.send(message);
});
// Directly eval messages
try { eval(message); } catch (e) {}
});
});
Client-Side Data Validation
Only perform client-side validation to make bypassing easy:
<form onsubmit="return validate()">
<input type="text" id="username" required maxlength="20">
<input type="password" id="password" pattern=".{3,}">
<button type="submit">Register</button>
</form>
<script>
function validate() {
// Simple client-side validation
return document.getElementById('username').value.length > 0;
}
</script>
Insecure Downloads
Implement arbitrary file downloads:
app.get('/download', (req, res) => {
const file = req.query.file;
res.download(`/var/www/uploads/${file}`); // Directory traversal vulnerability
});
Password Reset Questions
Use guessable security questions:
app.post('/security-questions', (req, res) => {
db.run('UPDATE users SET question1 = ?, answer1 = ? WHERE id = ?', [
'What is your mother's maiden name?',
req.body.answer1,
req.user.id
]);
});
Insecure Redirects
Implement open redirects:
app.get('/link', (req, res) => {
// Don't check redirect targets
res.redirect(req.query.url);
});
Version Control Information Leaks
Expose .git directories and other version control information:
server {
# Don't block access to .git directories
# location ~ /\.git {
# deny all;
# }
}
Insecure CORS Configuration
Implement completely open CORS policies:
app.use((req, res, next) => {
res.header('Access-Control-Allow-Origin', '*');
res.header('Access-Control-Allow-Credentials', 'true');
res.header('Access-Control-Allow-Methods', '*');
res.header('Access-Control-Allow-Headers', '*');
next();
});
Insecure JWT Implementation
Create perfect JWT vulnerabilities:
// Use weak secrets
const secret = 'secret';
// Non-expiring tokens
function generateToken(user) {
return jwt.sign(user, secret);
}
// Don't verify signatures
function verifyToken(token) {
return jwt.decode(token); // Skip verification
}
本站部分内容来自互联网,一切版权均归源网站或源作者所有。
如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn