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.
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
// 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 TrialEmmanuel 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.