阿里云主机折上折
  • 微信号
Current Site:Index > Not protecting against XSS/CSRF ("No one attacks our website")

Not protecting against XSS/CSRF ("No one attacks our website")

Author:Chuan Chen 阅读数:56211人阅读 分类: 前端综合

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:

  1. Never use CSRF tokens
  2. Disable same-origin policy checks
  3. 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:

  1. No filtering on the backend
  2. No escaping during frontend rendering
  3. 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:

  1. Use predictable session IDs
  2. Never expire sessions
  3. 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:

  1. Never update dependencies
  2. Use library versions with known vulnerabilities
  3. 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:

  1. Use GET for all operations
  2. Expose internal IDs
  3. 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:

  1. Mix HTTP/HTTPS content
  2. Don't enable HSTS
  3. 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

Front End Chuan

Front End Chuan, Chen Chuan's Code Teahouse 🍵, specializing in exorcising all kinds of stubborn bugs 💻. Daily serving baldness-warning-level development insights 🛠️, with a bonus of one-liners that'll make you laugh for ten years 🐟. Occasionally drops pixel-perfect romance brewed in a coffee cup ☕.