Skip to content

Security Best Practices

Security is built into Easy Deploy by default, but following these best practices will help you maintain the highest level of protection for your applications and data.

Easy Deploy provides security features out of the box:

  • HTTPS Everywhere: Automatic SSL certificates for all applications
  • Network Isolation: Applications run in isolated environments
  • Encrypted Storage: All data encrypted at rest
  • Secure Defaults: Security-first configuration out of the box
  • Easy Deploy Provides: Infrastructure security, platform security, network security
  • You Are Responsible For: Application security, data security, access management, secure coding practices

All applications include free SSL certificates:

  • Let’s Encrypt: Automatic certificate provisioning and renewal
  • Multiple Domains: Support for multiple domains per application
  • Wildcard Certificates: Available for Enterprise plans
  • Perfect Forward Secrecy: Advanced encryption protocols
.easydeploy.yml
security:
ssl:
force_https: true
min_tls_version: "1.2"
hsts:
enabled: true
max_age: 31536000
include_subdomains: true
preload: true

Upload your own SSL certificates (Pro plan):

Terminal window
curl -X POST \
-H "Authorization: Bearer YOUR_API_KEY" \
https://api.easydeploy.com/v1/applications/APP_ID/ssl

Implement security headers for enhanced protection:

Terminal window
// Express.js security headers
const helmet = require('helmet');
app.use(helmet({
contentSecurityPolicy: {
directives: {
defaultSrc: ["'self'"],
styleSrc: ["'self'", "'unsafe-inline'"],
scriptSrc: ["'self'"],
imgSrc: ["'self'", "data:", "https:"],
},
},
hsts: {
maxAge: 31536000,
includeSubDomains: true,
preload: true
}
}));

Implement secure user authentication:

Terminal window
const jwt = require('jsonwebtoken');
const bcrypt = require('bcrypt');
// Login endpoint
app.post('/login', async (req, res) => {
const { email, password } = req.body;
// Find user
const user = await User.findOne({ email });
if (!user) {
return res.status(401).json({ error: 'Invalid credentials' });
}
// Verify password
const isValid = await bcrypt.compare(password, user.password);
if (!isValid) {
return res.status(401).json({ error: 'Invalid credentials' });
}
// Generate JWT
const token = jwt.sign(
{ userId: user.id, email: user.email },
process.env.JWT_SECRET,
{ expiresIn: '24h' }
);
res.json({ token, user: { id: user.id, email: user.email } });
});
// Auth middleware
function authenticateToken(req, res, next) {
const authHeader = req.headers['authorization'];
const token = authHeader && authHeader.split(' ')[1];
if (!token) {
return res.status(401).json({ error: 'Access token required' });
}
jwt.verify(token, process.env.JWT_SECRET, (err, user) => {
if (err) {
return res.status(403).json({ error: 'Invalid token' });
}
req.user = user;
next();
});
}
Terminal window
const passport = require('passport');
const GoogleStrategy = require('passport-google-oauth20').Strategy;
passport.use(new GoogleStrategy({
clientID: process.env.GOOGLE_CLIENT_ID,
clientSecret: process.env.GOOGLE_CLIENT_SECRET,
callbackURL: "/auth/google/callback"
}, async (accessToken, refreshToken, profile, done) => {
try {
// Find or create user
let user = await User.findOne({ googleId: profile.id });
if (!user) {
user = await User.create({
googleId: profile.id,
email: profile.emails[0].value,
name: profile.displayName
});
}
return done(null, user);
} catch (error) {
return done(error, null);
}
}));

Implement role-based access control:

Terminal window
// Role-based authorization middleware
function authorize(roles = []) {
return (req, res, next) => {
if (!req.user) {
return res.status(401).json({ error: 'Authentication required' });
}
if (roles.length && !roles.includes(req.user.role)) {
return res.status(403).json({ error: 'Insufficient permissions' });
}
next();
};
}
// Usage
app.get('/admin', authenticateToken, authorize(['admin']), (req, res) => {
res.json({ message: 'Admin only content' });
});
app.get('/user', authenticateToken, authorize(['user', 'admin']), (req, res) => {
res.json({ message: 'User content' });
});

Secure management of sensitive data:

  • Encryption at Rest: All environment variables encrypted
  • Access Control: Team-based access permissions
  • Audit Logging: Track all variable changes
  • Secrets Rotation: Regular rotation of sensitive values

Secure your database connections:

Terminal window
// Secure database configuration
const dbConfig = {
connectionString: process.env.DATABASE_URL,
ssl: {
rejectUnauthorized: false,
sslmode: 'require'
},
max: 20, // connection pool limit
idleTimeoutMillis: 30000,
connectionTimeoutMillis: 2000,
};
// Parameterized queries (prevent SQL injection)
const getUser = async (userId) => {
const query = 'SELECT * FROM users WHERE id = $1';
const result = await db.query(query, [userId]);
return result.rows[0];
};
// Input validation
const { body, validationResult } = require('express-validator');
app.post('/user', [
body('email').isEmail().normalizeEmail(),
body('password').isLength({ min: 8 }).matches(/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)/),
body('name').trim().escape()
], (req, res) => {
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(400).json({ errors: errors.array() });
}
// Process validated input
});

Secure your API endpoints:

Terminal window
const rateLimit = require('express-rate-limit');
// General rate limiting
const generalLimiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15 minutes
max: 100, // limit each IP to 100 requests per windowMs
message: 'Too many requests from this IP'
});
// Strict rate limiting for auth endpoints
const authLimiter = rateLimit({
windowMs: 15 * 60 * 1000,
max: 5, // limit each IP to 5 requests per windowMs
skipSuccessfulRequests: true
});
app.use('/api/', generalLimiter);
app.use('/auth/', authLimiter);
Terminal window
const cors = require('cors');
app.use(cors({
origin: process.env.ALLOWED_ORIGINS?.split(',') || 'https://yourdomain.com',
credentials: true,
methods: ['GET', 'POST', 'PUT', 'DELETE'],
allowedHeaders: ['Content-Type', 'Authorization']
}));
Terminal window
// Request validation middleware
const validateApiKey = (req, res, next) => {
const apiKey = req.headers['x-api-key'];
if (!apiKey) {
return res.status(401).json({ error: 'API key required' });
}
// Validate API key
if (!isValidApiKey(apiKey)) {
return res.status(401).json({ error: 'Invalid API key' });
}
next();
};
// Content type validation
const validateJson = (req, res, next) => {
if (req.method === 'POST' || req.method === 'PUT') {
if (!req.is('application/json')) {
return res.status(400).json({ error: 'Content-Type must be application/json' });
}
}
next();
};

Configure network access rules:

.easydeploy.yml
security:
firewall:
inbound:
- port: 443
protocol: tcp
source: "0.0.0.0/0"
- port: 80
protocol: tcp
source: "0.0.0.0/0"
redirect_to: 443
outbound:
- port: 443
protocol: tcp
destination: "0.0.0.0/0"
- port: 53
protocol: udp
destination: "0.0.0.0/0"

Easy Deploy includes DDoS protection:

  • Traffic Analysis: Real-time traffic pattern analysis
  • Rate Limiting: Automatic rate limiting for suspicious traffic
  • Geographic Filtering: Block traffic from specific regions
  • Challenge-Response: CAPTCHA challenges for suspicious requests

Restrict access by IP address:

Terminal window
curl -X POST \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"allowed_ips": ["192.168.1.0/24", "10.0.0.0/8"],
"blocked_ips": ["192.168.1.100"]
}' \
https://api.easydeploy.com/v1/applications/APP_ID/ip-restrictions

Always validate and sanitize user input:

Terminal window
const validator = require('validator');
const xss = require('xss');
// Input sanitization
function sanitizeInput(input) {
if (typeof input !== 'string') return input;
// Remove XSS
input = xss(input);
// Trim whitespace
input = input.trim();
return input;
}
// Validation functions
function validateEmail(email) {
return validator.isEmail(email);
}
function validatePassword(password) {
return validator.isLength(password, { min: 8 }) &&
/[A-Z]/.test(password) &&
/[a-z]/.test(password) &&
/\d/.test(password) &&
/[!@#$%^&*]/.test(password);
}
function validateUrl(url) {
return validator.isURL(url, {
protocols: ['http', 'https'],
require_protocol: true
});
}

Implement security headers:

Terminal window
// Security headers middleware
app.use((req, res, next) => {
// Prevent XSS attacks
res.setHeader('X-XSS-Protection', '1; mode=block');
// Prevent clickjacking
res.setHeader('X-Frame-Options', 'DENY');
// Prevent MIME type sniffing
res.setHeader('X-Content-Type-Options', 'nosniff');
// Referrer policy
res.setHeader('Referrer-Policy', 'strict-origin-when-cross-origin');
// Content Security Policy
res.setHeader('Content-Security-Policy',
"default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'"
);
next();
});

Secure error handling:

Terminal window
// Production error handler
app.use((err, req, res, next) => {
// Log error details (server-side only)
console.error('Error:', {
message: err.message,
stack: err.stack,
url: req.url,
method: req.method,
ip: req.ip,
userAgent: req.get('User-Agent')
});
// Send generic error response (don't leak sensitive info)
const isDevelopment = process.env.NODE_ENV === 'development';
res.status(err.status || 500).json({
error: 'Internal Server Error',
message: isDevelopment ? err.message : 'Something went wrong',
...(isDevelopment && { stack: err.stack })
});
});

Log security-relevant events:

Terminal window
const auditLogger = require('./audit-logger');
// Authentication events
app.post('/login', async (req, res) => {
const { email } = req.body;
try {
const user = await authenticateUser(email, password);
auditLogger.log('user.login.success', {
userId: user.id,
email: user.email,
ip: req.ip,
userAgent: req.get('User-Agent')
});
res.json({ token, user });
} catch (error) {
auditLogger.log('user.login.failure', {
email,
ip: req.ip,
userAgent: req.get('User-Agent'),
reason: error.message
});
res.status(401).json({ error: 'Invalid credentials' });
}
});
// Data access events
app.get('/user/:id', authenticateToken, async (req, res) => {
const { id } = req.params;
auditLogger.log('user.data.access', {
accessedBy: req.user.id,
targetUser: id,
ip: req.ip,
timestamp: new Date().toISOString()
});
const user = await User.findById(id);
res.json(user);
});

Implement data protection features:

Terminal window
// Data export (Right to portability)
app.get('/user/:id/export', authenticateToken, authorize(['user', 'admin']), async (req, res) => {
const userData = await User.findById(req.params.id).lean();
const userActivity = await Activity.find({ userId: req.params.id }).lean();
const exportData = {
user: userData,
activities: userActivity,
exportedAt: new Date().toISOString()
};
res.setHeader('Content-Type', 'application/json');
res.setHeader('Content-Disposition', 'attachment; filename=user-data.json');
res.json(exportData);
});
// Data deletion (Right to erasure)
app.delete('/user/:id', authenticateToken, authorize(['admin']), async (req, res) => {
const userId = req.params.id;
// Delete user data across all systems
await User.findByIdAndDelete(userId);
await Activity.deleteMany({ userId });
await UserPreferences.deleteMany({ userId });
auditLogger.log('user.data.deleted', {
deletedBy: req.user.id,
targetUser: userId,
timestamp: new Date().toISOString()
});
res.json({ message: 'User data deleted successfully' });
});

Set up alerts for security events:

Terminal window
# Security alert configuration
security_alerts:
- name: "Multiple Failed Logins"
condition: "failed_logins > 5 within 10 minutes from same IP"
action: "block_ip"
notification: ["[email protected]"]
- name: "Suspicious API Usage"
condition: "api_requests > 1000 per minute from single IP"
action: "rate_limit"
notification: ["[email protected]"]
- name: "Admin Account Access"
condition: "admin login from new location"
action: "require_2fa"
notification: ["[email protected]"]

Automated security scanning:

  • Dependency Scanning: Check for vulnerable packages
  • Code Analysis: Static security analysis
  • Container Scanning: Scan Docker images for vulnerabilities
  • Infrastructure Scanning: Check for misconfigurations
  1. Detection: Automated alerts and monitoring
  2. Assessment: Evaluate scope and impact
  3. Containment: Isolate affected systems
  4. Investigation: Analyze logs and evidence
  5. Recovery: Restore normal operations
  6. Lessons Learned: Improve security measures
Terminal window
# Emergency: Rotate all API keys
curl -X POST \
-H "Authorization: Bearer YOUR_API_KEY" \
https://api.easydeploy.com/v1/applications/APP_ID/rotate-keys
# Emergency: Enable maintenance mode
curl -X POST \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"enabled": true, "message": "Maintenance in progress"}' \
https://api.easydeploy.com/v1/applications/APP_ID/maintenance
# Emergency: Block suspicious IP
curl -X POST \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"ip": "192.168.1.100", "reason": "suspicious activity"}' \
https://api.easydeploy.com/v1/applications/APP_ID/block-ip
  • Enable HTTPS for all endpoints
  • Implement proper authentication
  • Validate all user inputs
  • Set up rate limiting
  • Configure security headers
  • Review environment variables
  • Scan dependencies for vulnerabilities
  • Monitor security alerts
  • Review access logs regularly
  • Update dependencies
  • Rotate secrets periodically
  • Conduct security audits
  • Test incident response procedures
  • Monitor compliance requirements

Need help with security? Check our troubleshooting guide or contact our security team.