3 保护后端
3.1 输入验证和清理(服务器端)
- 始终在服务器端验证和清理用户输入,以防止 SQL 注入或其他代码注入攻击。即使前端完成了输入验证,后端验证也是必需的。
// 使用 Express Validator 中间件进行输入验证
import { body, validationResult } from "express-validator";
const validateUserInput = [
body("email").isEmail().withMessage("请输入有效的电子邮件地址"),
body("name").isLength({ min: 3 }).withMessage("姓名至少需要3个字符"),
body("password").isLength({ min: 6 }).withMessage("密码必须超过5个字符"),
// 验证结果中间件
(req, res, next) => {
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(400).json({ errors: errors.array()[0].msg });
}
next();
},
];
export default validateUserInput;
// 在路由文件中应用验证中间件
// register 是添加新用户的 POST 请求函数
router.route("/register").post(validateUserInput, register);
3.2 身份验证和授权
- 使用强大的身份验证机制(如 OAuth)和安全令牌(如 JWT)进行会话管理。
- 实施基于角色的访问控制(RBAC),避免在应用程序中硬编码权限。
3.3 密码安全
- 使用强大的哈希算法(如 bcrypt 或 Argon2)安全地存储密码。不要存储明文密码。
- 如有必要,实施多因素身份验证(MFA)以增加安全性。
// 在注册函数中哈希密码
const register = async (req: Request, res: Response) => {
const { email, password, name } = req.body;
const hashedPassword = await bcrypt.hash(password, 10);
createUser(email, name, hashedPassword); // 将新用户添加到数据库的函数
};
// 登录请求期间比较密码和哈希值
const login = async (req: Request, res: Response) => {
const { email, password } = req.body;
const existingUser = await query(findUserByEmail, [email]);
if (!existingUser[0])
return res.status(404).json({
message: `未找到 ${email}!如果没有账户,请注册`,
});
const matchPassword = await bcrypt.compare(
password,
existingUser[0].password
);
// 生成 JWT 令牌
const expirationTime = 180;
const accessToken = jwt.sign(
{ id: existingUser[0].id },
process.env.ACCESS_TOKEN_SECRET!,
{
expiresIn: expirationTime + "s",
}
);
};
3.4 速率限制(Rate Limiting)
- 为了防止暴力攻击,对敏感终端节点(如登录路由)实施速率限制和基于 IP 的限制。
const express = require('express');
const rateLimit = require('express-rate-limit');
const app = express();
// 定义速率限制规则:每15分钟100个请求
const limiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15分钟
max: 100, // 每个IP限制100个请求
message: '此IP的请求过多,请稍后再试。', // 自定义消息
standardHeaders: true, // `RateLimit-*` 头
legacyHeaders: false,
});
// 将速率限制中间件应用于所有请求
app.use(limiter);
// 也可以将其应用于特定路由
app.post('/login', limiter, (req, res) => {
res.send('登录路由');
});
app.listen(3000, () => {
console.log('服务器正在3000端口运行');
});
3.5 节流(Throttling)
- 与速率限制类似,但它不是完全阻止请求,而是在达到特定阈值后减慢请求的处理速度。这可以避免服务过载。
示例:如果用户每秒发出 10 个以上的 API 请求,则您需要减慢对每个后续请求的响应速度,以确保系统不会不堪重负。
3.6 数据加密
- 对静态和传输中的敏感数据进行加密。确保数据库和其他存储机制对敏感信息进行加密。
const crypto = require('crypto');
// 加密设置
const encryptionKey = crypto.randomBytes(32); // AES-256 密钥
const iv = crypto.randomBytes(16); // 初始化向量
// 加密函数
function encrypt(text) {
const cipher = crypto.createCipheriv('aes-256-cbc', encryptionKey, iv);
let encrypted = cipher.update(text, 'utf8', 'hex');
encrypted += cipher.final('hex');
return `${iv.toString('hex')}:${encrypted}`; // 存储 IV 和加密文本
}
// POST 请求
app.post('/user', (req, res) => {
const { creditCard, email } = req.body;
// 加密卡号
const encryptedCard = encrypt(creditCard);
// 将加密后的数据保存到数据库
const user = new User({
creditCard: encryptedCard,
email: email
});
user.save().then(() => {
res.send('用户保存成功');
}).catch(err => {
res.status(500).send('保存用户时出错');
});
});
当您需要显示真实数据(在本例中为卡号)时,您可以在GET请求中使用 decrypt 函数。
// 解密函数
function decrypt(encryptedData) {
const [ivHex, encryptedText] = encryptedData.split(':');
const ivBuffer = Buffer.from(ivHex, 'hex');
const decipher = crypto.createDecipheriv('aes-256-cbc', encryptionKey, ivBuffer);
let decrypted = decipher.update(encryptedText, 'hex', 'utf8');
decrypted += decipher.final('utf8');
return decrypted;
}
// GET 请求
app.get('/user/:id', (req, res) => {
const userId = req.params.id;
User.findById(userId).then(user => {
if (!user) return res.status(404).send('User not found');
// 解密用户卡号
const decryptedCard = decrypt(user.creditCard);
res.json({
creditCard: decryptedCard ,
email: user.email
});
}).catch(err => {
res.status(500).send('Error retrieving user');
});
});
3.7 安全标头
- 应用 HTTP 安全标头,如 Strict-Transport-Security、X-Frame-Options、X-XSS-Protection 和 X-Content-Type-Options,以防止各种常见漏洞。
const express = require('express');
const helmet = require('helmet');
const app = express();
app.use(helmet());
app.listen(3000, () => {
console.log('服务器正在3000端口运行');
});
另一种实现方式是手动设置 Headers without Helmet,如下所示。
app.use((req, res, next) => {
res.setHeader('Content-Security-Policy', "default-src 'self'; script-src 'self' https://trusted.cdn.com");
res.setHeader('X-Frame-Options', 'DENY');
res.setHeader('X-Content-Type-Options', 'nosniff');
res.setHeader('Strict-Transport-Security', 'max-age=31536000; includeSubDomains');
res.setHeader('X-XSS-Protection', '1; mode=block');
res.setHeader('Referrer-Policy', 'no-referrer');
res.setHeader('Permissions-Policy', 'geolocation=(self), microphone=()');
next();
});
3.8 CSRF 保护
- 实施跨站点请求伪造(CSRF)保护机制,例如 CSRF 令牌,以防止在您的应用程序中进行未经授权的操作。
CSRF攻击的工作原理:
- 用户登录到您的应用程序,服务器设置会话 Cookie 或身份验证令牌。
- 然后,用户访问恶意网站,同时仍在后台登录您的应用。
- 恶意网站使用登录用户的凭据(会话 cookie)向您的应用程序发送请求,以执行意外操作,例如转账或更改帐户详细信息。
为了防止 CSRF 攻击,您需要确保从前端发出的请求经过身份验证并来自受信任的来源。防止 CSRF 的最常见方法是使用 CSRF 令牌。
使用 cookie 解析器很容易实现,express.urlencoded 中间件。首先安装npm install csurf cookie-parser。
const express = require('express');
const cookieParser = require('cookie-parser');
const csrf = require('csurf');
const app = express();
// 使用 cookie-parser 存储 CSRF 令牌(如果使用基于 cookie 的令牌)
app.use(cookieParser());
// 初始化 CSRF 中间件
const csrfProtection = csrf({ cookie: true });
// 使用 body-parser 解析表单数据
app.use(express.urlencoded({ extended: false }));
// 在这里定义路由
app.listen(3000, () => {
console.log('服务器正在3000端口运行');
});
3.9 日志记录和监控
- 确保正确记录与安全相关的事件,例如失败的登录尝试和潜在的 XSS 攻击。此外,为异常行为设置监控和警报。
4 结论
本文的目标是强调在开发全栈 JavaScript 应用程序时必须考虑的关键安全原则,并提供这些原则的实用实施方法。我们探讨了前端和后端的最佳安全实践,以及如何在应用程序中有效地整合这些措施。通过深入理解这些安全原则,开发者可以构建更加健壮和安全的应用程序。此外,这些知识在准备求职面试时极为宝贵,尤其是在讨论应用程序开发中的各种安全措施和最佳实践时,能够展示您的专业性和对细节的关注。
希望本文提供的见解和示例能够帮助您在开发过程中做出更明智的安全决策,并确保您的应用程序能够抵御潜在的安全威胁。
版权声明:
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若内容造成侵权、违法违规、事实不符,请将相关资料发送至xkadmin@xkablog.com进行投诉反馈,一经查实,立即处理!
转载请注明出处,原文链接:https://www.xkablog.com/hd-aq/4489.html