阿里云主机折上折
  • 微信号
Current Site:Index > Data validation and input filtering

Data validation and input filtering

Author:Chuan Chen 阅读数:41353人阅读 分类: Node.js

The Necessity of Data Validation

Data validation is the first line of defense for web application security. Unvalidated user input can lead to security vulnerabilities such as SQL injection and cross-site scripting (XSS) attacks. Express, as a popular Node.js framework, directly handles user input data when processing HTTP requests, making it essential to strictly validate all incoming data.

// Dangerous example: Directly using unvalidated request body
app.post('/login', (req, res) => {
  const username = req.body.username;
  const password = req.body.password;
  // Using these values directly for database queries
});

Basic Validation Techniques

Type Checking

JavaScript's dynamic typing requires explicit data type checks:

function validateNumber(input) {
  if (typeof input !== 'number' || isNaN(input)) {
    throw new Error('Must input a valid number');
  }
  return Number(input);
}

Regular Expression Validation

Suitable for complex format validation, such as emails and URLs:

const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;

function validateEmail(email) {
  if (!emailRegex.test(email)) {
    throw new Error('Invalid email format');
  }
  return email.trim();
}

Express Middleware Validation

Using 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() });
    }
    // Process valid data
  }
);

Input Filtering Techniques

HTML Tag Filtering

Basic measures to prevent XSS attacks:

const sanitizeHtml = require('sanitize-html');

const clean = sanitizeHtml(dirtyHtml, {
  allowedTags: ['b', 'i', 'em', 'strong', 'a'],
  allowedAttributes: {
    'a': ['href']
  },
  allowedIframeHostnames: ['www.youtube.com']
});

SQL Injection Protection

Use parameterized queries instead of string concatenation:

// Wrong way
const query = `SELECT * FROM users WHERE username = '${username}'`;

// Correct way
const query = 'SELECT * FROM users WHERE username = ?';
connection.query(query, [username], (error, results) => {});

File Upload Validation

File type and content validation are equally important:

const multer = require('multer');
const upload = multer({
  limits: { fileSize: 1024 * 1024 * 5 }, // 5MB limit
  fileFilter: (req, file, cb) => {
    if (!file.originalname.match(/\.(jpg|jpeg|png|gif)$/)) {
      return cb(new Error('Only image files are allowed'));
    }
    cb(null, true);
  }
});

app.post('/upload', upload.single('avatar'), (req, res) => {
  // Process uploaded file
});

Custom Validators

Create reusable validation logic:

const validatePassword = (value) => {
  if (value.length < 8) {
    throw new Error('Password must be at least 8 characters');
  }
  if (!/[A-Z]/.test(value)) {
    throw new Error('Password must contain an uppercase letter');
  }
  if (!/[0-9]/.test(value)) {
    throw new Error('Password must contain a number');
  }
  return value;
};

app.post('/change-password', 
  body('newPassword').custom(validatePassword),
  // ...other route logic
);

Deep Object Validation

Handling nested data structures:

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 }),
  // ...validation error handling
);

Validation Error Handling

Unified validation error handling:

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);
});

Performance Considerations

Validation can impact performance; be mindful of:

// Avoid recompiling regular expressions in routes
const patterns = {
  username: /^[a-z0-9_-]{3,16}$/,
  password: /^(?=.*[A-Z])(?=.*[0-9]).{8,}$/
};

// Use early returns to avoid unnecessary validation
function validateUser(user) {
  if (!user) return false;
  if (!patterns.username.test(user.username)) return false;
  // Other validations...
  return true;
}

Client-Side vs. Server-Side Validation

Both must remain consistent:

// Client-side validation
function validateForm() {
  const username = document.getElementById('username').value;
  if (!/^[a-z0-9_-]{3,16}$/.test(username)) {
    showError('Username must be 3-16 alphanumeric characters');
    return false;
  }
  return true;
}

// Server-side must repeat these validations

Security Headers Setup

Additional defensive measures:

const helmet = require('helmet');
app.use(helmet());
app.use(helmet.contentSecurityPolicy({
  directives: {
    defaultSrc: ["'self'"],
    scriptSrc: ["'self'", "'unsafe-inline'"],
    styleSrc: ["'self'", "'unsafe-inline'"]
  }
}));

Logging

Log suspicious inputs:

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);
}

Testing Validation Logic

Automated testing of validation rules:

const request = require('supertest');
const app = require('../app');

describe('User Registration Validation', () => {
  it('Should reject invalid email', async () => {
    const res = await request(app)
      .post('/register')
      .send({ email: 'invalid', password: 'Valid123!' });
    expect(res.statusCode).toEqual(400);
  });
});

本站部分内容来自互联网,一切版权均归源网站或源作者所有。

如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn

Front End Chuan

Front End Chuan, Chen Chuan's Code Teahouse 🍵, specializing in exorcising all kinds of stubborn bugs 💻. Daily serving baldness-warning-level development insights 🛠️, with a bonus of one-liners that'll make you laugh for ten years 🐟. Occasionally drops pixel-perfect romance brewed in a coffee cup ☕.