Opensource Hosting Platform Wiki
Open-source hosting platform for servers, billing and support.
Brought to you by:
autobotsolution
This comprehensive security guide covers all security procedures, best practices, and configurations for the HostingCo system.
The HostingCo system implements multiple layers of security to protect user data, prevent unauthorized access, and ensure system integrity.
// backend/src/middleware/auth.ts
import jwt from 'jsonwebtoken';
const secureTokenConfig = {
secret: process.env.JWT_SECRET, // Use strong, random secret
expiresIn: '24h',
algorithm: 'HS256',
issuer: 'hostingco',
audience: 'hostingco-users'
};
// Generate secure token
const generateToken = (user) => {
return jwt.sign(
{
id: user.id,
email: user.email,
role: user.role,
permissions: user.permissions
},
secureTokenConfig.secret,
{
expiresIn: secureTokenConfig.expiresIn,
algorithm: secureTokenConfig.algorithm,
issuer: secureTokenConfig.issuer,
audience: secureTokenConfig.audience
}
);
};
// Verify JWT token with strict validation
const verifyToken = (token) => {
return jwt.verify(token, process.env.JWT_SECRET, {
algorithms: ['HS256'],
issuer: 'hostingco',
audience: 'hostingco-users'
});
};
// backend/src/utils/auth.ts
import bcrypt from 'bcryptjs';
const hashPassword = async (password) => {
const saltRounds = 12; // High salt rounds for security
return await bcrypt.hash(password, saltRounds);
};
const verifyPassword = async (password, hash) => {
return await bcrypt.compare(password, hash);
};
// Password strength validation
const validatePasswordStrength = (password) => {
const minLength = 8;
const hasUpperCase = /[A-Z]/.test(password);
const hasLowerCase = /[a-z]/.test(password);
const hasNumbers = /\d/.test(password);
const hasSpecialChar = /[!@#$%^&*(),.?":{}|<>]/.test(password);
return {
isValid: password.length >= minLength && hasUpperCase && hasLowerCase && hasNumbers && hasSpecialChar,
errors: [
password.length < minLength ? 'Password must be at least 8 characters' : null,
!hasUpperCase ? 'Password must contain uppercase letter' : null,
!hasLowerCase ? 'Password must contain lowercase letter' : null,
!hasNumbers ? 'Password must contain number' : null,
!hasSpecialChar ? 'Password must contain special character' : null
].filter(Boolean)
};
};
// backend/src/utils/roles.ts
export const ROLES = {
SUPER_ADMIN: 'super_admin',
ADMIN: 'admin',
SUPPORT: 'support',
USER: 'user',
READ_ONLY: 'read_only'
};
export const PERMISSIONS = {
// User permissions
USER_READ: 'user:read',
USER_WRITE: 'user:write',
USER_DELETE: 'user:delete',
// Server permissions
SERVER_READ: 'server:read',
SERVER_WRITE: 'server:write',
SERVER_DELETE: 'server:delete',
SERVER_ADMIN: 'server:admin',
// Billing permissions
BILLING_READ: 'billing:read',
BILLING_WRITE: 'billing:write',
BILLING_DELETE: 'billing:delete',
// Support permissions
SUPPORT_READ: 'support:read',
SUPPORT_WRITE: 'support:write',
SUPPORT_DELETE: 'support:delete',
// System permissions
SYSTEM_READ: 'system:read',
SYSTEM_WRITE: 'system:write',
SYSTEM_ADMIN: 'system:admin'
};
export const ROLE_PERMISSIONS = {
[ROLES.SUPER_ADMIN]: Object.values(PERMISSIONS),
[ROLES.ADMIN]: [
PERMISSIONS.USER_READ, PERMISSIONS.USER_WRITE, PERMISSIONS.USER_DELETE,
PERMISSIONS.SERVER_READ, PERMISSIONS.SERVER_WRITE, PERMISSIONS.SERVER_DELETE,
PERMISSIONS.BILLING_READ, PERMISSIONS.BILLING_WRITE,
PERMISSIONS.SUPPORT_READ, PERMISSIONS.SUPPORT_WRITE,
PERMISSIONS.SYSTEM_READ
],
[ROLES.SUPPORT]: [
PERMISSIONS.USER_READ,
PERMISSIONS.SERVER_READ, PERMISSIONS.SERVER_WRITE,
PERMISSIONS.SUPPORT_READ, PERMISSIONS.SUPPORT_WRITE,
PERMISSIONS.BILLING_READ
],
[ROLES.USER]: [
PERMISSIONS.USER_READ, PERMISSIONS.USER_WRITE,
PERMISSIONS.SERVER_READ, PERMISSIONS.SERVER_WRITE,
PERMISSIONS.BILLING_READ,
PERMISSIONS.SUPPORT_READ, PERMISSIONS.SUPPORT_WRITE
],
[ROLES.READ_ONLY]: [
PERMISSIONS.USER_READ,
PERMISSIONS.SERVER_READ,
PERMISSIONS.BILLING_READ,
PERMISSIONS.SUPPORT_READ
]
};
// backend/src/middleware/permissions.ts
export const requirePermission = (permission) => {
return (req, res, next) => {
const user = req.user;
if (!user) {
return res.status(401).json({
success: false,
error: { code: 'AUTHENTICATION_ERROR', message: 'Authentication required' }
});
}
const userPermissions = ROLE_PERMISSIONS[user.role] || [];
if (!userPermissions.includes(permission)) {
return res.status(403).json({
success: false,
error: { code: 'AUTHORIZATION_ERROR', message: 'Insufficient permissions' }
});
}
next();
};
};
// Usage example
router.get('/admin/users',
authenticateToken,
requirePermission(PERMISSIONS.USER_READ),
getUsers
);
// backend/src/utils/validation.ts
import Joi from 'joi';
import DOMPurify from 'isomorphic-dompurify';
// Sanitize HTML content
const sanitizeHtml = (content) => {
return DOMPurify.sanitize(content, {
ALLOWED_TAGS: ['p', 'br', 'strong', 'em', 'u', 'ol', 'ul', 'li'],
ALLOWED_ATTR: ['class'],
ALLOW_DATA_ATTR: false
});
};
// Validation schemas
export const Schemas = {
user: 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(100).required(),
phone: Joi.string().pattern(/^[+]?[\d\s\-\(\)]+$/).optional(),
company: Joi.string().max(100).optional()
}),
server: Joi.object({
name: Joi.string().min(2).max(100).required(),
plan: Joi.string().valid('basic', 'pro', 'enterprise').required(),
location: Joi.string().valid('us-east-1', 'us-west-1', 'eu-west-1', 'asia-east-1').required()
}),
supportTicket: Joi.object({
subject: Joi.string().min(5).max(200).required(),
description: Joi.string().min(10).max(2000).required(),
priority: Joi.string().valid('low', 'medium', 'high', 'urgent').default('medium'),
category: Joi.string().valid('technical', 'billing', 'general').default('general')
})
};
// Validation middleware
export const validateRequest = (schema) => {
return (req, res, next) => {
const { error, value } = schema.validate(req.body, {
abortEarly: false,
stripUnknown: true
});
if (error) {
return res.status(400).json({
success: false,
error: {
code: 'VALIDATION_ERROR',
message: 'Input validation failed',
details: error.details.map(detail => ({
field: detail.path.join('.'),
message: detail.message
}))
}
});
}
// Sanitize string fields
Object.keys(value).forEach(key => {
if (typeof value[key] === 'string') {
value[key] = sanitizeHtml(value[key]);
}
});
req.body = value;
next();
};
};
// Use parameterized queries with Knex.js
const getUserByEmail = async (email) => {
return await knex('users')
.where({ email }) // Parameterized query
.first();
};
// Never use string interpolation for queries
// BAD: await knex.raw(`SELECT * FROM users WHERE email = '${email}'`);
// GOOD: await knex('users').where({ email });
// frontend/src/utils/security.ts
import DOMPurify from 'dompurify';
// Sanitize user-generated content
export const sanitizeContent = (content) => {
return DOMPurify.sanitize(content, {
ALLOWED_TAGS: ['p', 'br', 'strong', 'em', 'u', 'ol', 'ul', 'li', 'a'],
ALLOWED_ATTR: ['href', 'class', 'target'],
ALLOW_DATA_ATTR: false
});
};
// Escape HTML in templates
export const escapeHtml = (text) => {
const div = document.createElement('div');
div.textContent = text;
return div.innerHTML;
};
// backend/src/utils/encryption.ts
import crypto from 'crypto';
class EncryptionService {
constructor() {
this.algorithm = 'aes-256-gcm';
this.secretKey = crypto.scryptSync(process.env.ENCRYPTION_KEY, 'salt', 32);
}
encrypt(text) {
const iv = crypto.randomBytes(16);
const cipher = crypto.createCipher(this.algorithm, this.secretKey);
cipher.setAAD(Buffer.from('hostingco', 'utf8'));
let encrypted = cipher.update(text, 'utf8', 'hex');
encrypted += cipher.final('hex');
const authTag = cipher.getAuthTag();
return {
encrypted,
iv: iv.toString('hex'),
authTag: authTag.toString('hex')
};
}
decrypt(encryptedData) {
const decipher = crypto.createDecipher(this.algorithm, this.secretKey);
decipher.setAAD(Buffer.from('hostingco', 'utf8'));
decipher.setAuthTag(Buffer.from(encryptedData.authTag, 'hex'));
let decrypted = decipher.update(encryptedData.encrypted, 'hex', 'utf8');
decrypted += decipher.final('utf8');
return decrypted;
}
}
export const encryptionService = new EncryptionService();
# Encrypt sensitive environment variables
echo "DATABASE_URL=postgresql://user:pass@host/db" | openssl enc -aes-256-cbc -base64 -pass pass:your-encryption-key
# Store encrypted value in .env
DATABASE_ENCRYPTED=U2FsdGVkX1+...
# Decrypt in application
const decryptEnvVar = (encryptedValue) => {
return execSync(`echo "${encryptedValue}" | openssl enc -aes-256-cbc -d -base64 -pass pass:${process.env.ENCRYPTION_KEY}`).toString();
};
# /etc/nginx/sites-available/hostingco
server {
listen 443 ssl http2;
server_name yourdomain.com www.yourdomain.com;
# SSL Configuration
ssl_certificate /etc/ssl/certs/yourdomain.crt;
ssl_certificate_key /etc/ssl/private/yourdomain.key;
# Modern SSL Configuration
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-RSA-AES256-GCM-SHA512:DHE-RSA-AES256-GCM-SHA512:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-GCM-SHA384;
ssl_prefer_server_ciphers off;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;
# Security Headers
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; font-src 'self'; connect-src 'self' wss:;" always;
# Application Configuration
location / {
root /var/www/hostingco/frontend/dist;
try_files $uri $uri/ /index.html;
}
location /api {
proxy_pass http://localhost:3003;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
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;
proxy_cache_bypass $http_upgrade;
# Security headers for API
add_header Access-Control-Allow-Origin $http_origin always;
add_header Access-Control-Allow-Credentials true always;
add_header Access-Control-Allow-Methods 'GET, POST, PUT, DELETE, OPTIONS' always;
add_header Access-Control-Allow-Headers 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range,Authorization' always;
}
}
# UFW (Uncomplicated Firewall) setup
sudo ufw default deny incoming
sudo ufw default allow outgoing
# Allow essential services
sudo ufw allow ssh
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
# Rate limiting for SSH
sudo ufw limit ssh
# Enable firewall
sudo ufw enable
# Check status
sudo ufw status verbose
# Advanced iptables rules
sudo iptables -A INPUT -p tcp --dport 22 -m limit --limit 3/min --limit-burst 3 -j ACCEPT
sudo iptables -A INPUT -p tcp --dport 22 -j DROP
# Rate limiting for HTTP/HTTPS
sudo iptables -A INPUT -p tcp --dport 80 -m limit --limit 25/minute --limit-burst 100 -j ACCEPT
sudo iptables -A INPUT -p tcp --dport 443 -m limit --limit 25/minute --limit-burst 100 -j ACCEPT
# Rate limiting configuration
http {
# Rate limiting
limit_req_zone $binary_remote_addr zone=api:10m rate=10r/s;
limit_req_zone $binary_remote_addr zone=login:10m rate=1r/s;
limit_req_zone $binary_remote_addr zone=upload:10m rate=2r/s;
# Connection limiting
limit_conn_zone $binary_remote_addr zone=conn_limit_per_ip:10m;
server {
# Apply rate limiting
location /api/auth/login {
limit_req zone=login burst=5 nodelay;
proxy_pass http://localhost:3003;
}
location /api {
limit_req zone=api burst=20 nodelay;
limit_conn conn_limit_per_ip 10;
proxy_pass http://localhost:3003;
}
location /api/upload {
limit_req zone=upload burst=5 nodelay;
proxy_pass http://localhost:3003;
}
}
}
// backend/src/utils/audit.ts
export const logActivity = async (activity) => {
const {
userId,
action,
resource,
resourceId,
details = {},
ipAddress,
userAgent
} = activity;
await knex('activity_logs').insert({
user_id: userId,
action,
resource,
resource_id: resourceId,
details,
ip_address: ipAddress,
user_agent: userAgent,
timestamp: new Date()
});
// Log to external security system
if (process.env.SECURITY_WEBHOOK_URL) {
await fetch(process.env.SECURITY_WEBHOOK_URL, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
timestamp: new Date().toISOString(),
level: 'INFO',
action,
userId,
resource,
resourceId,
ipAddress,
userAgent
})
});
}
};
// Usage in routes
router.post('/api/auth/login', async (req, res) => {
const { email, password } = req.body;
// ... authentication logic ...
await logActivity({
userId: user.id,
action: 'login',
resource: 'auth',
details: { email, success: true },
ipAddress: req.ip,
userAgent: req.get('User-Agent')
});
});
// backend/src/utils/security-monitor.ts
export const detectSuspiciousActivity = async (activity) => {
const { userId, ipAddress, action } = activity;
// Check for multiple failed logins
if (action === 'login_failed') {
const recentFailures = await knex('activity_logs')
.where({
ip_address: ipAddress,
action: 'login_failed'
})
.where('timestamp', '>', new Date(Date.now() - 15 * 60 * 1000)) // Last 15 minutes
.count();
if (recentFailures[0].count >= 5) {
await handleSuspiciousActivity({
type: 'brute_force_attack',
ipAddress,
details: { failures: recentFailures[0].count }
});
}
}
// Check for unusual access patterns
const recentActivity = await knex('activity_logs')
.where({ user_id: userId })
.where('timestamp', '>', new Date(Date.now() - 60 * 60 * 1000)) // Last hour
.orderBy('timestamp', 'desc');
// Detect multiple IP addresses for same user
const uniqueIPs = new Set(recentActivity.map(log => log.ip_address));
if (uniqueIPs.size > 3) {
await handleSuspiciousActivity({
type: 'multiple_ip_access',
userId,
details: { ipCount: uniqueIPs.size, ips: Array.from(uniqueIPs) }
});
}
};
export const handleSuspiciousActivity = async (event) => {
// Log security event
console.error('SECURITY ALERT:', event);
// Block IP if necessary
if (event.type === 'brute_force_attack') {
await blockTemporaryIP(event.ipAddress, '1 hour');
}
// Send notification
await sendSecurityAlert(event);
// Create security ticket
if (event.severity === 'HIGH') {
await createSecurityTicket(event);
}
};
// backend/src/middleware/security.ts
export const securityHeaders = (req, res, next) => {
// Prevent clickjacking
res.setHeader('X-Frame-Options', 'SAMEORIGIN');
// Prevent MIME type sniffing
res.setHeader('X-Content-Type-Options', 'nosniff');
// Enable XSS protection
res.setHeader('X-XSS-Protection', '1; mode=block');
// Control referrer information
res.setHeader('Referrer-Policy', 'strict-origin-when-cross-origin');
// Content Security Policy
res.setHeader('Content-Security-Policy', [
"default-src 'self'",
"script-src 'self' 'unsafe-inline'",
"style-src 'self' 'unsafe-inline'",
"img-src 'self' data: https:",
"font-src 'self'",
"connect-src 'self' wss:",
"frame-ancestors 'none'",
"base-uri 'self'",
"form-action 'self'"
].join('; '));
// HSTS (HTTPS only)
if (req.secure) {
res.setHeader('Strict-Transport-Security', 'max-age=31536000; includeSubDomains');
}
next();
};
// backend/src/middleware/ip-whitelist.ts
export const ipWhitelist = (allowedIPs) => {
return (req, res, next) => {
const clientIP = req.ip || req.connection.remoteAddress;
if (!allowedIPs.includes(clientIP)) {
return res.status(403).json({
success: false,
error: {
code: 'ACCESS_DENIED',
message: 'IP address not whitelisted'
}
});
}
next();
};
};
// Usage for admin routes
router.get('/admin/users',
authenticateToken,
requirePermission(PERMISSIONS.USER_READ),
ipWhitelist(['127.0.0.1', '::1', '192.168.1.0/24']),
getUsers
);
// backend/src/middleware/session-security.ts
export const sessionSecurity = {
// Session timeout
timeout: 24 * 60 * 60 * 1000, // 24 hours
// Maximum concurrent sessions per user
maxConcurrentSessions: 3,
// Check session validity
validateSession: async (token, req) => {
const decoded = jwt.decode(token);
const user = await getUserById(decoded.id);
// Check if user is still active
if (!user || !user.is_active) {
throw new Error('User account deactivated');
}
// Check session blacklist
const isBlacklisted = await redis.get(`blacklist:${token}`);
if (isBlacklisted) {
throw new Error('Token has been revoked');
}
// Check concurrent sessions
const sessionCount = await redis.scard(`sessions:${user.id}`);
if (sessionCount > sessionSecurity.maxConcurrentSessions) {
throw new Error('Too many concurrent sessions');
}
return user;
},
// Revoke session
revokeSession: async (token) => {
const decoded = jwt.decode(token);
const ttl = decoded.exp - Math.floor(Date.now() / 1000);
await redis.setex(`blacklist:${token}`, ttl, 'revoked');
await redis.srem(`sessions:${decoded.id}`, token);
}
};
#!/bin/bash
# security-incident-response.sh
# 1. Isolate affected systems
echo "Isolating affected systems..."
sudo ufw deny all
sudo systemctl stop nginx
# 2. Preserve evidence
echo "Preserving evidence..."
mkdir -p /tmp/security-incident/$(date +%Y%m%d_%H%M%S)
cp -r /var/log/nginx /tmp/security-incident/$(date +%Y%m%d_%H%M%S)/
cp -r backend/logs /tmp/security-incident/$(date +%Y%m%d_%H%M%S)/
# 3. Block malicious IPs
echo "Blocking malicious IPs..."
MALICIOUS_IPS=$(grep "Failed login" /var/log/auth.log | awk '{print $NF}' | sort | uniq -c | sort -nr | head -10 | awk '{print $2}')
for ip in $MALICIOUS_IPS; do
sudo ufw deny from $ip
done
# 4. Rotate secrets
echo "Rotating secrets..."
openssl rand -base64 32 > /tmp/new_jwt_secret
# Update application configuration
# 5. Notify security team
echo "Notifying security team..."
curl -X POST https://hooks.slack.com/services/YOUR/SLACK/WEBHOOK \
-H 'Content-type: application/json' \
--data '{"text":"Security incident detected. Check logs for details."}'
#!/bin/bash
# security-scan.sh
# Vulnerability scan
echo "Running vulnerability scan..."
nmap -sV --script vuln localhost
# Dependency vulnerability check
echo "Checking for vulnerable dependencies..."
npm audit --audit-level high
# SSL certificate check
echo "Checking SSL certificates..."
ssl-cert-check -a -c /etc/ssl/certs/
# File integrity check
echo "Checking file integrity..."
find /var/www/hostingco -type f -exec sha256sum {} \; > /tmp/file_hashes.txt
diff /tmp/file_hashes.txt /etc/security/file_hashes.baseline || echo "File integrity compromised!"
# Log analysis for suspicious patterns
echo "Analyzing logs for suspicious patterns..."
grep -i "attack\|intrusion\|breach" /var/log/nginx/access.log | tail -20
grep "Failed password" /var/log/auth.log | tail -20
# Install security monitoring tools
sudo apt install fail2ban rkhunter chkrootkit
# Configure fail2ban
sudo cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local
sudo systemctl enable fail2ban
sudo systemctl start fail2ban
# Run rootkit detection
sudo rkhunter --check --skip-keypress
# Check for system vulnerabilities
sudo apt list --upgradable
# Install log analysis tools
sudo apt install goaccess logwatch
# Analyze web server logs
goaccess access.log -c
# Generate daily log reports
logwatch --detail High --mailto admin@yourdomain.com
# Install network security tools
sudo apt install nmap tcpdump wireshark
# Network scanning
nmap -sS -O localhost
# Monitor network traffic
sudo tcpdump -i any -w /tmp/network_capture.pcap
# SSL/TLS testing
testssl.sh https://yourdomain.com
Last updated: $(date)