阿里云主机折上折
  • 微信号
您当前的位置:网站首页 > 数据验证与输入过滤

数据验证与输入过滤

作者:陈川 阅读数:41352人阅读 分类: Node.js

数据验证的必要性

数据验证是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

前端川

前端川,陈川的代码茶馆🍵,专治各种不服的Bug退散符💻,日常贩卖秃头警告级的开发心得🛠️,附赠一行代码笑十年的摸鱼宝典🐟,偶尔掉落咖啡杯里泡开的像素级浪漫☕。‌