正在加载,请稍候…

Web安全:XSS、CSRF、SQL注入防护与安全头

全面的Web安全指南,涵盖XSS防护、CSRF保护、SQL注入防御、内容安全策略以及现代Web应用的安全头配置。

Web安全:XSS、CSRF、SQL注入防护与安全头

Web安全:完整防护指南

XSS防护

// React - 默认已进行HTML转义
// 危险:dangerouslySetInnerHTML
<div dangerouslySetInnerHTML={{ __html: userInput }} /> // 切勿对不可信输入使用

// 安全:使用前先清理
import DOMPurify from 'dompurify'

function SafeHTML({ content }) {
  const clean = DOMPurify.sanitize(content, {
    ALLOWED_TAGS: ['b', 'i', 'em', 'strong', 'p', 'br'],
    ALLOWED_ATTR: [],
  })
  return <div dangerouslySetInnerHTML={{ __html: clean }} />
}

// 服务端:转义所有用户内容
import escapeHtml from 'escape-html'

app.get('/profile', (req, res) => {
  const username = escapeHtml(req.query.username)
  res.send(`<h1>Welcome, ${username}!</h1>`)
})

// CSP头阻止内联脚本
app.use((req, res, next) => {
  res.setHeader('Content-Security-Policy',
    "default-src 'self'; " +
    "script-src 'self' 'nonce-{RANDOM}'; " +
    "style-src 'self' 'unsafe-inline'; " +
    "img-src 'self' data: https:; " +
    "connect-src 'self' https://api.example.com"
  )
  next()
})

Web安全:XSS、CSRF、SQL注入防护与安全头示意图

CSRF防护

import csrf from 'csurf'
import cookieParser from 'cookie-parser'

app.use(cookieParser())
app.use(csrf({ cookie: { httpOnly: true, secure: true, sameSite: 'strict' } }))

// 在表单中包含CSRF令牌
app.get('/form', (req, res) => {
  res.render('form', { csrfToken: req.csrfToken() })
})

// 前端:在AJAX请求中包含令牌
const token = document.querySelector('meta[name="csrf-token"]').content

fetch('/api/action', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'X-CSRF-Token': token,
  },
  body: JSON.stringify({ data: 'value' }),
})

// 单页应用的双重提交Cookie模式
function getCSRFToken() {
  return document.cookie.match(/XSRF-TOKEN=([^;]+)/)?.[1]
}

// SameSite cookie提供额外保护
res.cookie('session', token, {
  httpOnly: true,
  secure: true,
  sameSite: 'strict', // 阻止跨站请求
  maxAge: 3600000,
})

Web安全:XSS、CSRF、SQL注入防护与安全头示意图

SQL注入防护

// 绝不:字符串拼接
const query = `SELECT * FROM users WHERE id = ${userId}` // 易受攻击

// 始终:参数化查询
import { Pool } from 'pg'
const pool = new Pool()

async function getUser(userId) {
  const result = await pool.query(
    'SELECT id, name, email FROM users WHERE id = $1',
    [userId]  // 参数化 - 安全
  )
  return result.rows[0]
}

// 使用Prisma ORM - 默认安全
const user = await prisma.user.findUnique({ where: { id: userId } })

// 使用Zod进行输入验证
import { z } from 'zod'

const UserIdSchema = z.string().uuid()
const SearchSchema = z.object({
  query: z.string().max(100).regex(/^[a-zA-Z0-9 ]+$/),
  page: z.number().int().positive().max(1000),
})

app.get('/search', async (req, res) => {
  const { query, page } = SearchSchema.parse(req.query)
  const results = await searchProducts(query, page)
  res.json(results)
})

Web安全:XSS、CSRF、SQL注入防护与安全头示意图

使用Helmet.js配置安全头

import helmet from 'helmet'

app.use(helmet({
  contentSecurityPolicy: {
    directives: {
      defaultSrc: ["'self'"],
      scriptSrc: ["'self'", "'nonce-abc123'"],
      styleSrc: ["'self'", "'unsafe-inline'"],
      imgSrc: ["'self'", "data:", "https:"],
      upgradeInsecureRequests: [],
    },
  },
  hsts: {
    maxAge: 31536000,
    includeSubDomains: true,
    preload: true,
  },
  frameguard: { action: 'deny' },
  noSniff: true,
  referrerPolicy: { policy: 'strict-origin-when-cross-origin' },
}))

// 速率限制
import rateLimit from 'express-rate-limit'

const limiter = rateLimit({
  windowMs: 15 * 60 * 1000, // 15分钟
  max: 100,
  standardHeaders: true,
  legacyHeaders: false,
  message: { error: '请求过多,请稍后再试。' },
})

app.use('/api/', limiter)

const loginLimiter = rateLimit({
  windowMs: 60 * 60 * 1000, // 1小时
  max: 5,
  skipSuccessfulRequests: true,
})
app.use('/api/auth/login', loginLimiter)

输入验证与清理

import { z } from 'zod'
import validator from 'validator'

const RegisterSchema = z.object({
  email: z.string().email().toLowerCase().trim(),
  password: z.string()
    .min(12, '密码至少12个字符')
    .regex(/[A-Z]/, '必须包含大写字母')
    .regex(/[0-9]/, '必须包含数字')
    .regex(/[^A-Za-z0-9]/, '必须包含特殊字符'),
  username: z.string()
    .min(3).max(30)
    .regex(/^[a-zA-Z0-9_-]+$/, '仅允许字母、数字、连字符、下划线'),
})

// 文件上传验证
const FileSchema = z.object({
  mimetype: z.enum(['image/jpeg', 'image/png', 'image/webp']),
  size: z.number().max(5 * 1024 * 1024), // 最大5MB
})

function validateFile(file) {
  // 检查实际内容,而非仅MIME类型
  const magic = file.buffer.slice(0, 4)
  const isPNG = magic.equals(Buffer.from([0x89, 0x50, 0x4e, 0x47]))
  const isJPEG = magic[0] === 0xff && magic[1] === 0xd8
  return isPNG || isJPEG
}

安全检查清单

类别 控制措施
XSS CSP、输出编码、DOMPurify
CSRF SameSite cookie、CSRF令牌
注入 参数化查询、输入验证
Helmet.js、HSTS、X-Frame-Options
认证 速率限制、bcrypt、MFA