← Back to BlogSeptember 15, 20259 min read
SSLSecurityProduction

Security Best Practices

Essential security practices for deploying applications in production environments. Learn about SSL certificates, environment variables, and access controls to protect your applications and data.

EZ
Emmanuel Zenderock
Engineer @ Servelink

Security is not an afterthought—it's a fundamental requirement for any production application. With cyber threats becoming more sophisticated, implementing robust security practices from the start is crucial. In this comprehensive guide, we'll explore essential security practices for deploying applications in production environments.

Security First: Security should be integrated into every aspect of your development and deployment process, not treated as a separate concern.

SSL/TLS Configuration

Secure communication is the foundation of web application security. Proper SSL/TLS configuration protects data in transit and builds user trust.

Certificate Management

Use strong certificates and implement proper certificate management:

SSL Configuration Best Practices

# Nginx SSL Configuration
server {
    listen 443 ssl http2;
    server_name your-domain.com;
    
    # SSL Certificate
    ssl_certificate /etc/ssl/certs/your-domain.crt;
    ssl_certificate_key /etc/ssl/private/your-domain.key;
    
    # SSL Security Settings
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers ECDHE-RSA-AES256-GCM-SHA512:DHE-RSA-AES256-GCM-SHA512:ECDHE-RSA-AES256-GCM-SHA384;
    ssl_prefer_server_ciphers off;
    
    # HSTS (HTTP Strict Transport Security)
    add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;
    
    # Security Headers
    add_header X-Frame-Options DENY;
    add_header X-Content-Type-Options nosniff;
    add_header X-XSS-Protection "1; mode=block";
    add_header Referrer-Policy "strict-origin-when-cross-origin";
    
    # OCSP Stapling
    ssl_stapling on;
    ssl_stapling_verify on;
    ssl_trusted_certificate /etc/ssl/certs/ca-certificates.crt;
    
    location / {
        proxy_pass http://localhost:3000;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}

Environment Variables and Secrets Management

Never hardcode sensitive information in your application code. Use secure environment variable management:

Critical: Never commit secrets to version control. Use environment variables, secret management services, or encrypted configuration files.

Secure Environment Configuration

config/database.js
// Secure database configuration
const config = {
  development: {
    host: process.env.DB_HOST || 'localhost',
    port: process.env.DB_PORT || 5432,
    database: process.env.DB_NAME,
    username: process.env.DB_USER,
    password: process.env.DB_PASSWORD,
    ssl: process.env.NODE_ENV === 'production' ? {
      rejectUnauthorized: true,
      ca: process.env.DB_SSL_CA
    } : false
  },
  
  production: {
    host: process.env.DB_HOST,
    port: process.env.DB_PORT,
    database: process.env.DB_NAME,
    username: process.env.DB_USER,
    password: process.env.DB_PASSWORD,
    ssl: {
      rejectUnauthorized: true,
      ca: process.env.DB_SSL_CA,
      cert: process.env.DB_SSL_CERT,
      key: process.env.DB_SSL_KEY
    },
    pool: {
      max: 20,
      min: 5,
      acquire: 30000,
      idle: 10000
    }
  }
};

// Validate required environment variables
const requiredEnvVars = ['DB_HOST', 'DB_NAME', 'DB_USER', 'DB_PASSWORD'];
const missingVars = requiredEnvVars.filter(varName => !process.env[varName]);

if (missingVars.length > 0) {
  throw new Error(`Missing required environment variables: ${missingVars.join(', ')}`);
}

module.exports = config;

Authentication and Authorization

Implement robust authentication and authorization mechanisms to control access to your application:

JWT Token Security

// JWT implementation with security best practices
const jwt = require('jsonwebtoken');
const crypto = require('crypto');

class AuthService {
  constructor() {
    this.secretKey = process.env.JWT_SECRET;
    this.refreshSecretKey = process.env.JWT_REFRESH_SECRET;
    this.accessTokenExpiry = '15m';
    this.refreshTokenExpiry = '7d';
  }

  // Generate secure access token
  generateAccessToken(payload) {
    return jwt.sign(payload, this.secretKey, {
      expiresIn: this.accessTokenExpiry,
      issuer: 'your-app-name',
      audience: 'your-app-users'
    });
  }

  // Generate refresh token
  generateRefreshToken(payload) {
    return jwt.sign(payload, this.refreshSecretKey, {
      expiresIn: this.refreshTokenExpiry,
      issuer: 'your-app-name',
      audience: 'your-app-users'
    });
  }

  // Verify access token
  verifyAccessToken(token) {
    try {
      return jwt.verify(token, this.secretKey, {
        issuer: 'your-app-name',
        audience: 'your-app-users'
      });
    } catch (error) {
      throw new Error('Invalid or expired token');
    }
  }

  // Generate secure random tokens
  generateSecureToken(length = 32) {
    return crypto.randomBytes(length).toString('hex');
  }

  // Hash passwords with salt
  async hashPassword(password) {
    const salt = crypto.randomBytes(16).toString('hex');
    const hash = crypto.pbkdf2Sync(password, salt, 10000, 64, 'sha512').toString('hex');
    return `${salt}:${hash}`;
  }

  // Verify password
  async verifyPassword(password, hashedPassword) {
    const [salt, hash] = hashedPassword.split(':');
    const verifyHash = crypto.pbkdf2Sync(password, salt, 10000, 64, 'sha512').toString('hex');
    return hash === verifyHash;
  }
}

Input Validation and Sanitization

Always validate and sanitize user input to prevent injection attacks and data corruption:

Input Validation Middleware

// Input validation middleware
const Joi = require('joi');
const sanitizeHtml = require('sanitize-html');

// Validation schemas
const schemas = {
  userRegistration: Joi.object({
    email: Joi.string().email().required(),
    password: Joi.string().min(8).pattern(/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]/).required(),
    name: Joi.string().min(2).max(50).required(),
    age: Joi.number().integer().min(13).max(120).optional()
  }),
  
  userLogin: Joi.object({
    email: Joi.string().email().required(),
    password: Joi.string().required()
  })
};

// Validation middleware
const validateInput = (schema) => {
  return (req, res, next) => {
    const { error, value } = schema.validate(req.body);
    
    if (error) {
      return res.status(400).json({
        success: false,
        error: 'Validation error',
        details: error.details.map(detail => detail.message)
      });
    }
    
    // Sanitize HTML content
    if (value.name) {
      value.name = sanitizeHtml(value.name, {
        allowedTags: [],
        allowedAttributes: {}
      });
    }
    
    req.body = value;
    next();
  };
};

// Rate limiting middleware
const rateLimit = require('express-rate-limit');

const loginLimiter = rateLimit({
  windowMs: 15 * 60 * 1000, // 15 minutes
  max: 5, // limit each IP to 5 requests per windowMs
  message: 'Too many login attempts, please try again later',
  standardHeaders: true,
  legacyHeaders: false
});

Database Security

Secure your database connections and implement proper access controls:

Connection Security

  • • Use SSL/TLS for all connections
  • • Implement connection pooling
  • • Use strong authentication
  • • Regular security updates

Access Control

  • • Principle of least privilege
  • • Role-based access control
  • • Regular access reviews
  • • Audit logging

SQL Injection Prevention

// Safe database queries using parameterized statements
class UserRepository {
  constructor(db) {
    this.db = db;
  }

  // Safe user lookup
  async findUserByEmail(email) {
    const query = 'SELECT id, email, name FROM users WHERE email = $1';
    const result = await this.db.query(query, [email]);
    return result.rows[0];
  }

  // Safe user creation
  async createUser(userData) {
    const { email, name, hashedPassword } = userData;
    const query = `
      INSERT INTO users (email, name, password_hash, created_at)
      VALUES ($1, $2, $3, NOW())
      RETURNING id, email, name, created_at
    `;
    const result = await this.db.query(query, [email, name, hashedPassword]);
    return result.rows[0];
  }

  // Safe user update with validation
  async updateUser(userId, updateData) {
    const allowedFields = ['name', 'email'];
    const updates = [];
    const values = [];
    let paramCount = 1;

    for (const [key, value] of Object.entries(updateData)) {
      if (allowedFields.includes(key)) {
        updates.push(`${key} = $${paramCount}`);
        values.push(value);
        paramCount++;
      }
    }

    if (updates.length === 0) {
      throw new Error('No valid fields to update');
    }

    values.push(userId);
    const query = `
      UPDATE users 
      SET ${updates.join(', ')}, updated_at = NOW()
      WHERE id = $${paramCount}
      RETURNING id, email, name, updated_at
    `;
    
    const result = await this.db.query(query, values);
    return result.rows[0];
  }
}

Security Headers and CORS

Implement proper security headers and CORS policies to protect against common web vulnerabilities:

Security Headers Configuration

// Express.js security middleware
const helmet = require('helmet');
const cors = require('cors');

// Helmet configuration
app.use(helmet({
  contentSecurityPolicy: {
    directives: {
      defaultSrc: ["'self'"],
      styleSrc: ["'self'", "'unsafe-inline'"],
      scriptSrc: ["'self'"],
      imgSrc: ["'self'", "data:", "https:"],
      connectSrc: ["'self'"],
      fontSrc: ["'self'"],
      objectSrc: ["'none'"],
      mediaSrc: ["'self'"],
      frameSrc: ["'none'"]
    }
  },
  hsts: {
    maxAge: 31536000,
    includeSubDomains: true,
    preload: true
  }
}));

// CORS configuration
const corsOptions = {
  origin: function (origin, callback) {
    const allowedOrigins = process.env.ALLOWED_ORIGINS?.split(',') || ['http://localhost:3000'];
    
    if (!origin || allowedOrigins.includes(origin)) {
      callback(null, true);
    } else {
      callback(new Error('Not allowed by CORS'));
    }
  },
  credentials: true,
  optionsSuccessStatus: 200,
  methods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'],
  allowedHeaders: ['Content-Type', 'Authorization', 'X-Requested-With']
};

app.use(cors(corsOptions));

// Additional security headers
app.use((req, res, next) => {
  res.setHeader('X-Content-Type-Options', 'nosniff');
  res.setHeader('X-Frame-Options', 'DENY');
  res.setHeader('X-XSS-Protection', '1; mode=block');
  res.setHeader('Referrer-Policy', 'strict-origin-when-cross-origin');
  res.setHeader('Permissions-Policy', 'geolocation=(), microphone=(), camera=()');
  next();
});

Monitoring and Logging

Implement comprehensive security monitoring and logging to detect and respond to threats:

Security Logging

  • • Authentication attempts
  • • Authorization failures
  • • Suspicious activities
  • • Data access patterns

Monitoring

  • • Real-time threat detection
  • • Performance monitoring
  • • Error rate tracking
  • • Resource usage alerts

Incident Response

  • • Automated alerts
  • • Response procedures
  • • Forensic capabilities
  • • Recovery processes

Servelink Security Features

Servelink provides built-in security features to protect your applications:

Built-in Security

  • • Automatic SSL certificate management
  • • DDoS protection and rate limiting
  • • Web Application Firewall (WAF)
  • • Security scanning and vulnerability assessment
  • • Encrypted environment variables
  • • Network isolation and VPC support
  • • Compliance certifications (SOC 2, GDPR)

Security Checklist

Use this checklist to ensure your application meets security best practices:

Production Security Checklist

Infrastructure Security
  • SSL/TLS encryption enabled
  • Security headers configured
  • Environment variables secured
  • Database connections encrypted
Application Security
  • Input validation implemented
  • Authentication secured
  • Rate limiting configured
  • Security monitoring enabled

Conclusion

Security is an ongoing process that requires constant vigilance and regular updates. By implementing these best practices from the start, you can significantly reduce the risk of security breaches and protect your users' data.

Remember that security is not just about technology—it's also about processes, training, and maintaining a security-conscious culture within your team. Regular security audits and penetration testing can help identify and address vulnerabilities before they're exploited.

Secure your applications with Servelink

Deploy with confidence using Servelink's built-in security features and best practices.

Start Free Trial
EZ

Emmanuel Zenderock

Engineer @ Servelink

Emmanuel is a cybersecurity expert with over 2 years of experience in application security, penetration testing, and security architecture. He specializes in web application security and cloud security best practices.