认证、加密、防护详解 | 从零基础到精通 | 包含常见漏洞和防护方案
安全漏洞可能导致数据泄露、系统被攻击、用户隐私被侵犯。作为开发者,有责任保护用户的数据和系统的安全。
// 危险的做法
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) => {
// 处理结果
});// 危险的做法
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}`);
});// 在保存到数据库前清理
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 });
});// 攻击流程
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>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 });
});// 实现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();
}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: '服务器错误' });
}
});安全是一个持续的过程,需要在开发的每个阶段都要考虑。
现在你已经掌握了安全编程的核心知识。
安全没有完美,只有持续改进。定期审计、及时更新、持续学习是保持应用安全的关键。