Advanced Node.js Interview Questions on API Security

 

  1. How do you mitigate SQL/NoSQL injection attacks in a Node.js API?

    • Answer: Use ORM/ODM libraries like Sequelize or Mongoose that sanitize inputs. Validate and sanitize user input using libraries like express-validator.

  2. Explain JWT authentication and how to securely store tokens.

    • Answer: JWTs are signed tokens for stateless authentication. Store them in HTTPOnly cookies to prevent XSS attacks and use refresh tokens for renewing access tokens.

  3. How would you implement rate limiting and why is it important?

    • Answer: Use express-rate-limit to restrict repeated requests, preventing DDoS/brute-force attacks. Example:

      javascript

      const rateLimit = require('express-rate-limit');
      const limiter = rateLimit({ windowMs: 15 * 60 * 1000, max: 100 });
      app.use(limiter);
  4. What security headers does Helmet.js set, and why are they critical?

    • Answer: Helmet sets headers like Content-Security-PolicyX-Frame-Options, and Strict-Transport-Security to protect against XSS, clickjacking, and enforce HTTPS.

  5. How do you manage sensitive configuration data (e.g., API keys) in Node.js?

    • Answer: Use environment variables with dotenv and never commit .env files. Store secrets in secure vaults (e.g., AWS Secrets Manager) in production.


Example Scenario: Building a Secure Node.js API

Step 1: Initialize Project & Dependencies

bash

npm init -y
npm install express helmet bcrypt jsonwebtoken cors express-rate-limit express-validator mongoose cookie-parser

Step 2: Configure HTTPS

Use Let’s Encrypt for production. For development, generate a self-signed certificate:

javascript
const https = require('https');
const fs = require('fs');
const options = {
  key: fs.readFileSync('server.key'),
  cert: fs.readFileSync('server.cert')
};
const server = https.createServer(options, app);

Step 3: Security Middleware

javascript

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

const app = express();

// Middleware
app.use(helmet());
app.use(cors({ origin: 'https://trusted-domain.com' }));
app.use(express.json({ limit: '10kb' })); // Prevent large payloads
app.use(rateLimit({ windowMs: 15 * 60 * 1000, max: 100 })); // Rate limiting

Step 4: Database Setup (MongoDB/Mongoose)

javascript

const mongoose = require('mongoose');
mongoose.connect(process.env.DB_URI, { useNewUrlParser: true, useUnifiedTopology: true })
  .then(() => console.log('DB Connected!'))
  .catch(err => console.error('DB Connection Error:', err));

// User model with bcrypt password hashing
const userSchema = new mongoose.Schema({
  email: { type: String, unique: true },
  password: String,
  role: { type: String, enum: ['user', 'admin'] }
});

userSchema.pre('save', async function (next) {
  if (this.isModified('password')) {
    this.password = await bcrypt.hash(this.password, 12);
  }
  next();
});

Step 5: Authentication with JWT & HTTPOnly Cookies

javascript

const jwt = require('jsonwebtoken');
const cookieParser = require('cookie-parser');
app.use(cookieParser());

// Login route
app.post('/login', async (req, res) => {
  const { email, password } = req.body;
  const user = await User.findOne({ email });
  
  if (!user || !(await bcrypt.compare(password, user.password))) {
    return res.status(401).json({ error: 'Invalid credentials' });
  }

  const accessToken = jwt.sign({ id: user._id }, process.env.JWT_SECRET, { expiresIn: '15m' });
  const refreshToken = jwt.sign({ id: user._id }, process.env.JWT_REFRESH_SECRET, { expiresIn: '7d' });

  res.cookie('accessToken', accessToken, { httpOnly: true, secure: true, sameSite: 'Strict' });
  res.cookie('refreshToken', refreshToken, { httpOnly: true, secure: true, sameSite: 'Strict' });
  res.json({ role: user.role });
});

Step 6: Input Validation

javascript

const { body, validationResult } = require('express-validator');

app.post('/register', 
  [
    body('email').isEmail().normalizeEmail(),
    body('password').isLength({ min: 8 }).trim().escape()
  ],
  (req, res) => {
    const errors = validationResult(req);
    if (!errors.isEmpty()) return res.status(400).json({ errors: errors.array() });
    // Proceed with registration
  }
);

Step 7: Role-Based Authorization Middleware

javascript

const authorize = (roles) => (req, res, next) => {
  const token = req.cookies.accessToken;
  if (!token) return res.status(403).json({ error: 'Access denied' });

  jwt.verify(token, process.env.JWT_SECRET, (err, user) => {
    if (err || !roles.includes(user.role)) return res.status(403).json({ error: 'Unauthorized' });
    req.user = user;
    next();
  });
};

// Admin-only route
app.get('/admin', authorize(['admin']), (req, res) => {
  res.json({ message: 'Admin access granted' });
});

Step 8: Error Handling & Logging

javascript

// Central error handler
app.use((err, req, res, next) => {
  console.error(err.stack);
  res.status(500).json({ error: 'Internal server error' });
});

// Logging middleware (use Winston or Morgan in production)
app.use((req, res, next) => {
  console.log(`${req.method} ${req.path}`);
  next();
});

Step 9: CSRF Protection (Optional)

For APIs, use the SameSite=Strict cookie attribute. For forms, use packages like csurf:

javascript

const csrf = require('csurf');
const csrfProtection = csrf({ cookie: true });
app.use(csrfProtection);

Key Security Takeaways:

  1. HTTPS: Encrypt data in transit.

  2. Helmet.js: Secure HTTP headers.

  3. JWT in HTTPOnly Cookies: Mitigate XSS risks.

  4. Rate Limiting: Prevent brute-force attacks.

  5. Input Validation/Sanitization: Block injection attacks.

  6. Role-Based Access Control (RBAC): Least privilege principle.

  7. Environment Variables: Keep secrets out of code.

  8. Logging & Monitoring: Detect anomalies early.

No comments:

Post a Comment

latest ECMAScript proposals and releases (as of ECMAScript 2024), several enhancements have been made to built-in objects like Set

JavaScript continues to evolve, and in the latest ECMAScript proposals and releases (as of ECMAScript 2024), several enhancements have been ...

Best for you