跳转至

前端安全

前端安全

📚 章节目标

本章节将全面介绍前端安全的核心概念和技术,包括XSS、CSRF、CSP、安全最佳实践等,帮助学习者掌握前端安全的核心方法。

学习目标

  1. 理解前端安全的核心概念
  2. 掌握XSS攻击防护
  3. 掌握CSRF攻击防护
  4. 掌握CSP内容安全策略
  5. 理解前端安全最佳实践
  6. 掌握安全审计和测试

🔒 前端安全概述

1. 常见安全威胁

JavaScript
// 前端安全威胁
// 1. XSS (Cross-Site Scripting) - 跨站脚本攻击
// 2. CSRF (Cross-Site Request Forgery) - 跨站请求伪造
// 3. 点击劫持 - 恶意网站覆盖正常网站
// 4. 中间人攻击 - 拦截和修改通信
// 5. 数据泄露 - 敏感数据泄露

// 安全原则
// 1. 永远不要信任用户输入
// 2. 对所有输入进行验证和转义
// 3. 使用HTTPS加密通信
// 4. 实施最小权限原则
// 5. 定期进行安全审计

2. 安全检查清单

JavaScript
// 前端安全检查清单
const securityChecklist = {
  // 输入验证
  inputValidation: [
    '验证所有用户输入',
    '限制输入长度',
    '使用白名单验证',
  ],

  // 输出编码
  outputEncoding: [
    '对HTML输出进行编码',
    '对JavaScript输出进行编码',
    '对URL输出进行编码',
  ],

  // 认证和授权
  auth: [
    '使用强密码策略',
    '实施多因素认证',
    '定期更新会话令牌',
  ],

  // 通信安全
  communication: [
    '使用HTTPS',
    '实施CSP',
    '使用安全的Cookie',
  ],

  // 数据保护
  dataProtection: [
    '加密敏感数据',
    '不在本地存储敏感信息',
    '实施数据最小化原则',
  ],
};

🎭 XSS攻击防护

1. XSS攻击类型

1.1 存储型XSS

JavaScript
// 存储型XSS示例
// 恶意用户提交包含脚本的评论
const comment = '<script>alert("XSS")</script>';

// 服务器存储评论
database.saveComment(comment);

// 其他用户查看评论时执行脚本
function renderComment(comment) {
  // 不安全的渲染
  document.getElementById('comments').innerHTML = comment;
}

// 防护:对输出进行编码
function renderComment(comment) {
  const encoded = encodeHTML(comment);
  document.getElementById('comments').innerHTML = encoded;
}

function encodeHTML(str) {
  return str
    .replace(/&/g, '&')
    .replace(/</g, '<')
    .replace(/>/g, '>')
    .replace(/"/g, '"')
    .replace(/'/g, ''');
}

1.2 反射型XSS

JavaScript
// 反射型XSS示例
// URL参数包含恶意脚本
// http://example.com/search?q=<script>alert("XSS")</script>

// 服务器直接输出参数
function renderSearch(query) {
  // 不安全的渲染
  document.getElementById('results').innerHTML = `Search results for: ${query}`;
}

// 防护:对输出进行编码
function renderSearch(query) {
  const encoded = encodeHTML(query);
  document.getElementById('results').innerHTML = `Search results for: ${encoded}`;
}

1.3 DOM型XSS

JavaScript
// DOM型XSS示例
// 恶意URL
// http://example.com/#<img src=x onerror=alert("XSS")>

// JavaScript使用location.hash
const hash = window.location.hash;
document.getElementById('content').innerHTML = hash;

// 防护:对输出进行编码
const hash = window.location.hash;
const encoded = encodeHTML(hash);
document.getElementById('content').innerHTML = encoded;

2. XSS防护策略

2.1 输入验证

JavaScript
// 输入验证
function validateInput(input, type) {
  const patterns = {
    email: /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/,
    url: /^https?:\/\/.+/,
    username: /^[a-zA-Z0-9_]{3,20}$/,
  };

  return patterns[type]?.test(input) || false;  // ?.可选链:对象为null/undefined时安全返回undefined
}

// 使用示例
const email = 'user@example.com';
if (validateInput(email, 'email')) {
  console.log('Valid email');
}

2.2 输出编码

JavaScript
// 使用DOMPurify进行HTML净化
import DOMPurify from 'dompurify';

function sanitizeHTML(dirty) {
  return DOMPurify.sanitize(dirty);
}

// 使用示例
const dirty = '<script>alert("XSS")</script><p>Hello</p>';
const clean = sanitizeHTML(dirty);
console.log(clean); // <p>Hello</p>

// React自动转义
function Comment({ text }) {
  return <div>{text}</div>; // 自动转义
}

// Vue自动转义
<template>
  <div>{{ text }}</div> <!-- 自动转义 -->
</template>

2.3 CSP策略

JavaScript
// 设置CSP头
// Express.js — 使用nonce代替unsafe-inline(更安全)
const crypto = require('crypto');
app.use((req, res, next) => {
  const nonce = crypto.randomBytes(16).toString('base64');
  res.locals.nonce = nonce;
  res.setHeader(
    'Content-Security-Policy',
    `default-src 'self'; script-src 'self' 'nonce-${nonce}'; style-src 'self' 'nonce-${nonce}'; img-src 'self' data: https:; font-src 'self' data:; connect-src 'self' https:; frame-ancestors 'none';`
  );
  next();
});
// ⚠️ 避免使用 'unsafe-inline' 和 'unsafe-eval',它们会严重削弱CSP对XSS的防护

// HTML meta标签(生产环境建议通过HTTP头设置)
<meta http-equiv="Content-Security-Policy" content="
  default-src 'self';
  script-src 'self';
  style-src 'self';
  img-src 'self' data: https:;
  font-src 'self' data:;
  connect-src 'self' https:;
  frame-ancestors 'none';
">

🛡️ CSRF攻击防护

1. CSRF攻击原理

JavaScript
// CSRF攻击示例
// 用户登录银行网站
// https://bank.example.com/login

// 用户访问恶意网站
// 恶意网站包含隐藏表单
<form action="https://bank.example.com/transfer" method="POST">
  <input type="hidden" name="to" value="attacker">
  <input type="hidden" name="amount" value="10000">
</form>

<script>
  document.forms[0].submit();
</script>

// 用户浏览器自动提交表单
// 银行网站认为这是用户的合法请求

2. CSRF防护策略

2.1 CSRF Token

JavaScript
// 生成CSRF Token
import crypto from 'crypto';

function generateCSRFToken() {
  return crypto.randomBytes(32).toString('hex');
}

// 服务器设置Token
app.use((req, res, next) => {
  const token = generateCSRFToken();
  res.cookie('csrfToken', token, {
    httpOnly: false, // Double Submit Cookie模式需要前端JS读取,不能设为httpOnly
    secure: true,
    sameSite: 'strict',
  });
  req.csrfToken = token;
  next();
});

// 验证Token
function validateCSRFToken(req, res, next) {
  const token = req.cookies.csrfToken;
  const headerToken = req.headers['x-csrf-token'];

  if (token && headerToken && token === headerToken) {
    next();
  } else {
    res.status(403).json({ error: 'Invalid CSRF token' });
  }
}

// 前端发送Token
fetch('/api/transfer', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'X-CSRF-Token': getCookie('csrfToken'),
  },
  body: JSON.stringify({ to: 'user', amount: 100 }),
});
JavaScript
// 设置SameSite Cookie
app.use(session({
  secret: 'secret',
  cookie: {
    httpOnly: true,
    secure: true,
    sameSite: 'strict', // 'strict' | 'lax' | 'none'
  },
}));

// SameSite选项
// strict - 仅同站请求发送Cookie
// lax - 同站请求和顶级导航发送Cookie
// none - 所有请求都发送Cookie(需要Secure)

2.3 验证来源

JavaScript
// 验证Referer头
function validateReferer(req, res, next) {
  const referer = req.headers.referer;
  const allowedOrigins = ['https://example.com'];

  if (referer && allowedOrigins.some(origin => referer.startsWith(origin))) {
    next();
  } else {
    res.status(403).json({ error: 'Invalid referer' });
  }
}

// 验证Origin头
function validateOrigin(req, res, next) {
  const origin = req.headers.origin;
  const allowedOrigins = ['https://example.com'];

  if (origin && allowedOrigins.includes(origin)) {
    next();
  } else {
    res.status(403).json({ error: 'Invalid origin' });
  }
}

🚪 其他安全威胁

1. 点击劫持防护

JavaScript
// 点击劫持攻击
// 恶意网站使用iframe嵌入正常网站
// 诱导用户点击隐藏的按钮

// 防护:使用X-Frame-Options头
app.use((req, res, next) => {
  res.setHeader('X-Frame-Options', 'DENY');
  // 或者
  res.setHeader('X-Frame-Options', 'SAMEORIGIN');
  next();
});

// 防护:使用CSP frame-ancestors
app.use((req, res, next) => {
  res.setHeader('Content-Security-Policy', "frame-ancestors 'none'");
  next();
});

// 防护:JavaScript检测
if (window.self !== window.top) {
  window.top.location = window.self.location;
}

2. 中间人攻击防护

JavaScript
// 使用HTTPS
// 配置HTTPS服务器
const https = require('https');
const fs = require('fs');

const options = {
  key: fs.readFileSync('server.key'),
  cert: fs.readFileSync('server.crt'),
  ca: fs.readFileSync('ca.crt'),
};

https.createServer(options, (req, res) => {
  res.writeHead(200);
  res.end('Hello HTTPS!');
}).listen(443);

// HSTS (HTTP Strict Transport Security)
app.use((req, res, next) => {
  res.setHeader(
    'Strict-Transport-Security',
    'max-age=31536000; includeSubDomains; preload'
  );
  next();
});

3. 数据泄露防护

JavaScript
// 不在本地存储敏感信息
// 不好的做法
localStorage.setItem('password', 'secret123');
localStorage.setItem('token', 'abc123');

// 好的做法
// 使用HttpOnly Cookie
res.cookie('token', 'abc123', {
  httpOnly: true,
  secure: true,
  sameSite: 'strict',
});

// 加密敏感数据
import crypto from 'crypto';

function encrypt(text, key) {
  const iv = crypto.randomBytes(16);
  const cipher = crypto.createCipheriv('aes-256-cbc', key, iv);
  let encrypted = cipher.update(text, 'utf8', 'hex');
  encrypted += cipher.final('hex');
  return iv.toString('hex') + ':' + encrypted;
}

function decrypt(text, key) {
  const parts = text.split(':');
  const iv = Buffer.from(parts[0], 'hex');
  const encrypted = parts[1];
  const decipher = crypto.createDecipheriv('aes-256-cbc', key, iv);
  let decrypted = decipher.update(encrypted, 'hex', 'utf8');
  decrypted += decipher.final('utf8');
  return decrypted;
}

🔍 安全审计和测试

1. 安全审计工具

JavaScript
// 使用npm audit
npm audit

// 使用Snyk
npm install -g snyk
snyk test

// 使用OWASP ZAP
// 下载和运行OWASP ZAP
// 扫描应用程序

// 使用Burp Suite
// 下载和运行Burp Suite
// 代理和拦截请求

2. 安全测试

JavaScript
// XSS测试
function testXSS(input) {
  const xssPayloads = [
    '<script>alert("XSS")</script>',
    '<img src=x onerror=alert("XSS")>',
    '<svg onload=alert("XSS")>',
    'javascript:alert("XSS")',
  ];

  xssPayloads.forEach(payload => {  // 箭头函数:简洁的函数语法
    const result = sanitizeHTML(payload);
    if (result !== payload) {
      console.log(`XSS payload sanitized: ${payload}`);
    } else {
      console.error(`XSS payload not sanitized: ${payload}`);
    }
  });
}

// CSRF测试
function testCSRF() {
  // 测试是否验证CSRF Token
  // 测试是否验证Referer
  // 测试是否使用SameSite Cookie
}

📝 练习题

1. 基础题

题目1:实现XSS防护

JavaScript
// 实现HTML编码函数
function encodeHTML(str) {
  // 编码HTML特殊字符
}

// 使用示例
const input = '<script>alert("XSS")</script>';  // const不可重新赋值;let块级作用域变量
const encoded = encodeHTML(input);
console.log(encoded);

2. 进阶题

题目2:实现CSRF防护

JavaScript
// 生成CSRF Token
function generateCSRFToken() {
  // 生成随机Token
}

// 验证CSRF Token
function validateCSRFToken(req) {
  // 验证Token是否有效
}

3. 面试题

题目3:解释XSS和CSRF的区别

JavaScript
// 答案要点:
// XSS (Cross-Site Scripting):
// - 攻击者在网站中注入恶意脚本
// - 脚本在用户浏览器中执行
// - 可以窃取用户数据、执行恶意操作

// CSRF (Cross-Site Request Forgery):
// - 攻击者诱导用户发送请求
// - 请求使用用户的认证信息
// - 可以执行用户未授权的操作

// 防护:
// XSS - 输入验证、输出编码、CSP
// CSRF - CSRF Token、SameSite Cookie、验证来源

🎯 本章总结

本章节全面介绍了前端安全的核心概念和技术,包括XSS、CSRF、CSP、安全最佳实践等。关键要点:

  1. 安全威胁:理解常见的前端安全威胁
  2. XSS防护:掌握XSS攻击的防护策略
  3. CSRF防护:掌握CSRF攻击的防护策略
  4. 其他威胁:掌握点击劫持、中间人攻击、数据泄露的防护
  5. 安全审计:掌握安全审计和测试方法
  6. 最佳实践:理解前端安全最佳实践

下一步将深入学习前端架构设计。