数据验证与输入过滤
数据验证的必要性
数据验证是Web应用安全的第一道防线。未经验证的用户输入可能导致SQL注入、跨站脚本攻击(XSS)等安全漏洞。Express作为Node.js的流行框架,处理HTTP请求时直接接触用户输入数据,必须严格验证所有传入数据。
// 危险示例:直接使用未验证的请求体
app.post('/login', (req, res) => {
const username = req.body.username;
const password = req.body.password;
// 直接使用这些值进行数据库查询
});
基础验证技术
类型检查
JavaScript的动态类型特性要求开发者显式检查数据类型:
function validateNumber(input) {
if (typeof input !== 'number' || isNaN(input)) {
throw new Error('必须输入有效数字');
}
return Number(input);
}
正则表达式验证
适用于复杂格式验证,如电子邮件、URL等:
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
function validateEmail(email) {
if (!emailRegex.test(email)) {
throw new Error('无效的电子邮件格式');
}
return email.trim();
}
Express中间件验证
express-validator使用
const { body, validationResult } = require('express-validator');
app.post('/user',
body('username').isLength({ min: 5 }).trim().escape(),
body('email').isEmail().normalizeEmail(),
body('age').isInt({ min: 18 }),
(req, res) => {
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(400).json({ errors: errors.array() });
}
// 处理有效数据
}
);
输入过滤技术
HTML标签过滤
防止XSS攻击的基本措施:
const sanitizeHtml = require('sanitize-html');
const clean = sanitizeHtml(dirtyHtml, {
allowedTags: ['b', 'i', 'em', 'strong', 'a'],
allowedAttributes: {
'a': ['href']
},
allowedIframeHostnames: ['www.youtube.com']
});
SQL注入防护
使用参数化查询代替字符串拼接:
// 错误方式
const query = `SELECT * FROM users WHERE username = '${username}'`;
// 正确方式
const query = 'SELECT * FROM users WHERE username = ?';
connection.query(query, [username], (error, results) => {});
文件上传验证
文件类型和内容验证同样重要:
const multer = require('multer');
const upload = multer({
limits: { fileSize: 1024 * 1024 * 5 }, // 5MB限制
fileFilter: (req, file, cb) => {
if (!file.originalname.match(/\.(jpg|jpeg|png|gif)$/)) {
return cb(new Error('只允许图片文件'));
}
cb(null, true);
}
});
app.post('/upload', upload.single('avatar'), (req, res) => {
// 处理上传文件
});
自定义验证器
创建可复用的验证逻辑:
const validatePassword = (value) => {
if (value.length < 8) {
throw new Error('密码至少8个字符');
}
if (!/[A-Z]/.test(value)) {
throw new Error('密码必须包含大写字母');
}
if (!/[0-9]/.test(value)) {
throw new Error('密码必须包含数字');
}
return value;
};
app.post('/change-password',
body('newPassword').custom(validatePassword),
// ...其他路由逻辑
);
深度对象验证
处理嵌套数据结构:
const { body } = require('express-validator');
app.post('/order',
body('customer.name').notEmpty().trim(),
body('customer.email').isEmail(),
body('items.*.productId').isMongoId(),
body('items.*.quantity').isInt({ min: 1 }),
// ...验证错误处理
);
验证错误处理
统一处理验证错误:
app.use((err, req, res, next) => {
if (err instanceof ValidationError) {
return res.status(422).json({
errors: err.array().map(e => ({
param: e.param,
message: e.msg,
location: e.location
}))
});
}
next(err);
});
性能考虑
验证可能影响性能,需注意:
// 避免在路由中重复编译正则表达式
const patterns = {
username: /^[a-z0-9_-]{3,16}$/,
password: /^(?=.*[A-Z])(?=.*[0-9]).{8,}$/
};
// 使用提前返回避免不必要验证
function validateUser(user) {
if (!user) return false;
if (!patterns.username.test(user.username)) return false;
// 其他验证...
return true;
}
客户端与服务端验证
两者需保持一致:
// 客户端验证
function validateForm() {
const username = document.getElementById('username').value;
if (!/^[a-z0-9_-]{3,16}$/.test(username)) {
showError('用户名必须是3-16位字母数字');
return false;
}
return true;
}
// 服务端必须重复这些验证
安全头设置
补充防御措施:
const helmet = require('helmet');
app.use(helmet());
app.use(helmet.contentSecurityPolicy({
directives: {
defaultSrc: ["'self'"],
scriptSrc: ["'self'", "'unsafe-inline'"],
styleSrc: ["'self'", "'unsafe-inline'"]
}
}));
日志记录
记录可疑输入:
app.use((req, res, next) => {
if (req.body && containsMaliciousInput(req.body)) {
logSuspiciousActivity(req);
}
next();
});
function containsMaliciousInput(obj) {
return JSON.stringify(obj).match(/(<script>|SELECT \* FROM|DROP TABLE)/i);
}
测试验证逻辑
自动化测试验证规则:
const request = require('supertest');
const app = require('../app');
describe('用户注册验证', () => {
it('应拒绝无效邮箱', async () => {
const res = await request(app)
.post('/register')
.send({ email: 'invalid', password: 'Valid123!' });
expect(res.statusCode).toEqual(400);
});
});
本站部分内容来自互联网,一切版权均归源网站或源作者所有。
如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn
上一篇:Promise.prototype.finally()
下一篇:认证授权方案实现