📚 完整教程目录

前言:安全的重要性

🤔 为什么需要安全编程?

安全漏洞可能导致数据泄露、系统被攻击、用户隐私被侵犯。作为开发者,有责任保护用户的数据和系统的安全。

常见的安全威胁

安全开发的原则

⚠️ 重要:安全不是事后补救,而是从一开始就要考虑的问题。

OWASP Top 10

十大Web应用安全风险

  1. 注入攻击(Injection) - SQL注入、命令注入
  2. 身份验证失效(Broken Authentication) - 弱密码、会话管理不当
  3. 敏感数据泄露(Sensitive Data Exposure) - 未加密、传输不安全
  4. XML外部实体(XXE) - 解析不安全的XML
  5. 访问控制失效(Broken Access Control) - 权限验证不足
  6. 安全配置缺陷(Security Misconfiguration) - 默认配置、暴露信息
  7. XSS(Cross-Site Scripting) - 注入恶意脚本
  8. 不安全的反序列化(Insecure Deserialization) - 反序列化不安全数据
  9. 使用已知漏洞的组件(Using Components with Known Vulnerabilities) - 过时的库
  10. 不充分的日志和监控(Insufficient Logging & Monitoring) - 无法检测攻击

注入攻击防护

SQL注入

// 危险的做法 const username = req.body.username; const query = `SELECT * FROM users WHERE username = '${username}'`; // 攻击:username = "' OR '1'='1" // 安全的做法(参数化查询) const query = 'SELECT * FROM users WHERE username = ?'; db.query(query, [username], (err, results) => { // 处理结果 }); // 使用ORM const user = await User.findOne({ username: username });

命令注入防护

// 危险的做法 const filename = req.body.filename; exec(`cat ${filename}`); // 攻击:filename = "; rm -rf /" // 安全的做法 const { execFile } = require('child_process'); execFile('cat', [filename], (err, stdout) => { // 处理结果 });

防护方案

XSS防护

反射型XSS

// 危险的做法 app.get('/search', (req, res) => { const query = req.query.q; res.send(`Search results for: ${query}`); // 攻击:?q=<script>alert('XSS')</script> }); // 安全的做法 const escapeHtml = (text) => { const map = { '&': '&', '<': '<', '>': '>', '"': '"', "'": ''' }; return text.replace(/[&<>"']/g, m => map[m]); }; app.get('/search', (req, res) => { const query = escapeHtml(req.query.q); res.send(`Search results for: ${query}`); });

存储型XSS防护

// 在保存到数据库前清理 const sanitizeHtml = require('sanitize-html'); app.post('/comment', (req, res) => { const comment = sanitizeHtml(req.body.comment, { allowedTags: ['b', 'i', 'em', 'strong'], allowedAttributes: {} }); // 保存到数据库 Comment.create({ content: comment }); });

防护方案

CSRF防护

CSRF攻击原理

// 攻击流程 1. 用户登录银行网站 2. 用户访问恶意网站(在另一个标签页) 3. 恶意网站发送请求到银行网站 4. 由于用户已登录,请求被执行 // 防护:CSRF令牌 <form method="POST" action="/transfer"> <input type="hidden" name="csrf_token" value="abc123xyz"> <input type="text" name="amount"> <button type="submit">转账</button> </form>

CSRF防护实现

const csrf = require('csurf'); const cookieParser = require('cookie-parser'); app.use(cookieParser()); app.use(csrf({ cookie: true })); // 生成令牌 app.get('/form', (req, res) => { res.send(`<form method="POST" action="/transfer"> <input type="hidden" name="_csrf" value="${req.csrfToken()}"> <input type="text" name="amount"> <button type="submit">转账</button> </form>`); }); // 验证令牌 app.post('/transfer', (req, res) => { // csrf中间件自动验证 res.send('转账成功'); });

防护方案

认证和授权

密码安全

const bcrypt = require('bcrypt'); // 注册 app.post('/register', async (req, res) => { const { username, password } = req.body; // 验证密码强度 if (password.length < 12) { return res.status(400).json({ error: '密码过弱' }); } // 哈希密码 const hashedPassword = await bcrypt.hash(password, 10); // 保存到数据库 await User.create({ username, password: hashedPassword }); }); // 登录 app.post('/login', async (req, res) => { const { username, password } = req.body; const user = await User.findOne({ username }); if (!user) { return res.status(401).json({ error: '用户不存在' }); } // 验证密码 const isValid = await bcrypt.compare(password, user.password); if (!isValid) { return res.status(401).json({ error: '密码错误' }); } // 生成令牌 const token = jwt.sign({ userId: user.id }, process.env.JWT_SECRET); res.json({ token }); });

多因素认证(MFA)

// 实现TOTP(基于时间的一次性密码) const speakeasy = require('speakeasy'); // 生成密钥 const secret = speakeasy.generateSecret({ name: `MyApp (${email})` }); // 验证令牌 const isValid = speakeasy.totp.verify({ secret: secret.base32, encoding: 'base32', token: userToken, window: 2 });

授权控制

// 基于角色的访问控制(RBAC) const authorize = (roles) => { return (req, res, next) => { if (!req.user || !roles.includes(req.user.role)) { return res.status(403).json({ error: '无权限' }); } next(); }; }; app.delete('/users/:id', authenticate, authorize(['admin']), (req, res) => { // 删除用户 } );

加密和哈希

哈希函数

const crypto = require('crypto'); // SHA-256哈希 function hashPassword(password) { return crypto .createHash('sha256') .update(password) .digest('hex'); } // 使用盐值增强安全性 function hashWithSalt(password, salt) { return crypto .pbkdf2Sync(password, salt, 100000, 64, 'sha512') .toString('hex'); }

对称加密

const crypto = require('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(encryptedText, key) { const parts = encryptedText.split(':'); const iv = Buffer.from(parts[0], 'hex'); const decipher = crypto.createDecipheriv('aes-256-cbc', key, iv); let decrypted = decipher.update(parts[1], 'hex', 'utf8'); decrypted += decipher.final('utf8'); return decrypted; }

非对称加密

const crypto = require('crypto'); // 生成密钥对 const { publicKey, privateKey } = crypto.generateKeyPairSync('rsa', { modulusLength: 2048 }); // 加密 function encryptPublic(text, publicKey) { return crypto.publicEncrypt(publicKey, Buffer.from(text)).toString('hex'); } // 解密 function decryptPrivate(encrypted, privateKey) { return crypto.privateDecrypt(privateKey, Buffer.from(encrypted, 'hex')).toString(); }

安全通信

HTTPS配置

const https = require('https'); const fs = require('fs'); const options = { key: fs.readFileSync('private-key.pem'), cert: fs.readFileSync('certificate.pem'), // 强制HTTPS secureOptions: crypto.constants.SSL_OP_NO_TLSv1 | crypto.constants.SSL_OP_NO_TLSv1_1 }; https.createServer(options, app).listen(443);

安全头部

const helmet = require('helmet'); app.use(helmet()); // 手动配置 app.use((req, res, next) => { // 防止点击劫持 res.setHeader('X-Frame-Options', 'DENY'); // 防止MIME类型嗅探 res.setHeader('X-Content-Type-Options', 'nosniff'); // 启用XSS防护 res.setHeader('X-XSS-Protection', '1; mode=block'); // 内容安全策略 res.setHeader('Content-Security-Policy', "default-src 'self'"); // HSTS res.setHeader('Strict-Transport-Security', 'max-age=31536000; includeSubDomains'); next(); });

输入验证

验证框架

const { body, validationResult } = require('express-validator'); app.post('/register', [ body('email').isEmail(), body('password').isLength({ min: 12 }), body('username').matches(/^[a-zA-Z0-9_]+$/), body('age').isInt({ min: 18, max: 120 }) ], (req, res) => { const errors = validationResult(req); if (!errors.isEmpty()) { return res.status(400).json({ errors: errors.array() }); } // 处理请求 });

白名单验证

// 只允许特定的值 const allowedSortFields = ['name', 'email', 'created_at']; const sortField = req.query.sort; if (!allowedSortFields.includes(sortField)) { return res.status(400).json({ error: '无效的排序字段' }); } // 使用白名单构建查询 const query = User.find().sort({ [sortField]: 1 });

防护方案

实战项目:构建安全的应用

安全检查清单

安全代码示例

const express = require('express'); const helmet = require('helmet'); const rateLimit = require('express-rate-limit'); const { body, validationResult } = require('express-validator'); const bcrypt = require('bcrypt'); const app = express(); // 安全中间件 app.use(helmet()); // 速率限制 const limiter = rateLimit({ windowMs: 15 * 60 * 1000, max: 100 }); app.use(limiter); // 登录端点 app.post('/login', [ body('email').isEmail(), body('password').isLength({ min: 8 }) ], async (req, res) => { const errors = validationResult(req); if (!errors.isEmpty()) { return res.status(400).json({ errors: errors.array() }); } const { email, password } = req.body; try { const user = await User.findOne({ email }); if (!user) { // 不暴露用户是否存在 return res.status(401).json({ error: '邮箱或密码错误' }); } const isValid = await bcrypt.compare(password, user.password); if (!isValid) { return res.status(401).json({ error: '邮箱或密码错误' }); } // 生成安全的会话 const token = jwt.sign( { userId: user.id }, process.env.JWT_SECRET, { expiresIn: '1h' } ); res.json({ token }); } catch (error) { console.error('Login error:', error); res.status(500).json({ error: '服务器错误' }); } });
✨ 实战总结:

安全是一个持续的过程,需要在开发的每个阶段都要考虑。

🎉 安全编程学习完成

现在你已经掌握了安全编程的核心知识。

✅ 你现在可以:

🚀 下一步学习

  1. 学习渗透测试
  2. 学习安全审计
  3. 学习合规性(GDPR、CCPA等)
  4. 学习事件响应
💡 建议:

安全没有完美,只有持续改进。定期审计、及时更新、持续学习是保持应用安全的关键。