Securing Node.js API

From Logic Wiki
Jump to: navigation, search


Use HTTPS

HTTPS encrypts the data exchanged between the client and the server, preventing man-in-the-middle attacks. Ensure your API endpoints are only accessible over HTTPS.

To enable HTTPS, you’ll need an SSL certificate. Here’s a basic setup using the https module:

const https = require('https');
const fs = require('fs');
const express = require('express');
const app = express();

const options = {
  key: fs.readFileSync('path/to/your/private.key'),
  cert: fs.readFileSync('path/to/your/certificate.crt')
};

https.createServer(options, app).listen(443, () => {
  console.log('Server is running on https://localhost:443');
});

Validate Input

Always validate and sanitize user inputs to prevent SQL injection, XSS attacks, and other malicious inputs. Libraries like Joi and express-validator can help enforce validation rules.

Using Joi for validation:

const Joi = require('joi');

const schema = Joi.object({
  username: Joi.string().alphanum().min(3).max(30).required(),
  password: Joi.string().pattern(new RegExp('^[a-zA-Z0-9]{3,30}$')).required()
});

app.post('/register', (req, res, next) => {
  const { error } = schema.validate(req.body);
  if (error) {
    return res.status(400).send(error.details[0].message);
  }
  next();
});

Implement Authentication

Use robust authentication mechanisms such as OAuth, JWT (JSON Web Tokens), or API keys. JWT is particularly popular for its simplicity and security.

const jwt = require('jsonwebtoken');

// Generate a token
const token = jwt.sign({ userId: user.id }, 'your-secret-key', { expiresIn: '1h' });

// Middleware to verify a token
function authenticateToken(req, res, next) {
  const token = req.headers['authorization'];
  if (!token) return res.sendStatus(401);

  jwt.verify(token, 'your-secret-key', (err, user) => {
    if (err) return res.sendStatus(403);
    req.user = user;
    next();
  });
}

app.get('/protected', authenticateToken, (req, res) => {
  res.send('This is a protected route');
});

Enforce Authorization

Ensure that authenticated users have the necessary permissions to access specific resources. Role-based access control (RBAC) can help manage user roles and permissions effectively.

const roles = {
  admin: ['read', 'write', 'delete'],
  user: ['read', 'write']
};

function authorize(role, action) {
  return (req, res, next) => {
    const userRole = req.user.role;
    if (roles[userRole] && roles[userRole].includes(action)) {
      next();
    } else {
      res.sendStatus(403);
    }
  };
}

app.post('/admin', authenticateToken, authorize('admin', 'write'), (req, res) => {
  res.send('Admin route');
});

Rate Limiting

Implement rate limiting to prevent abuse and DDoS attacks. Libraries like express-rate-limit can help you set up rate limiting for your API endpoints.

const rateLimit = require('express-rate-limit');

const limiter = rateLimit({
  windowMs: 15 * 60 * 1000, // 15 minutes
  max: 100 // limit each IP to 100 requests per windowMs
});

app.use(limiter);

Secure Your Dependencies

Regularly update your dependencies and use tools like npm audit to identify and fix vulnerabilities in third-party packages.

Run the following command to check for vulnerabilities:

npm audit

Use Helmet for HTTP Headers

Helmet helps secure your API by setting various HTTP headers. It can prevent many common vulnerabilities.

const helmet = require('helmet');
app.use(helmet());

Content Security Policy (CSP)

Implement CSP to prevent XSS attacks by whitelisting trusted content sources. Helmet can also help with CSP.

app.use(helmet.contentSecurityPolicy({
  directives: {
    defaultSrc: ["'self'"],
    scriptSrc: ["'self'", 'trusted-cdn.com']
  }
}));

Logging and Monitoring

Maintain logs of all activities and monitor them for suspicious behavior. Tools like Winston for logging and PM2 for monitoring can be highly effective.

Using Winston for logging:

const winston = require('winston');

const logger = winston.createLogger({
  level: 'info',
  format: winston.format.json(),
  transports: [
    new winston.transports.File({ filename: 'error.log', level: 'error' }),
    new winston.transports.File({ filename: 'combined.log' })
  ]
});

app.use((req, res, next) => {
  logger.info(`HTTP ${req.method} ${req.url}`);
  next();
});

Using PM2 for monitoring:

pm2 start app.js --name "my-api"
pm2 monit

Regular Security Audits

Conduct regular security audits and penetration testing to identify and mitigate potential security risks.

You can use tools like nmap, OWASP ZAP, and Burp Suite for penetration testing. Regularly review your code and dependencies for vulnerabilities.