security: comprehensive security audit and remediation (20 fixes)
CRITICAL fixes: - Restrict X-View-Tenant impersonation to global admin only (was any admin) - Add authorization to subscription endpoints (was open to any user) - Make webhook signature verification mandatory (was skippable) - Remove databaseName from JWT payload (resolve server-side with cache) - Reduce body size limit from 1GB to 10MB (50MB for bulk CFDI) - Restrict .env file permissions to 600 HIGH fixes: - Add authorization to SAT cron endpoints (global admin only) - Add Content-Security-Policy and Permissions-Policy headers - Centralize isGlobalAdmin() utility with caching - Add rate limiting on auth endpoints (express-rate-limit) - Require authentication on logout endpoint MEDIUM fixes: - Replace Math.random() with crypto.randomBytes for temp passwords - Remove console.log of temporary passwords in production - Remove DB credentials from admin notification email - Add escapeHtml() to email templates (prevent HTML injection) - Add file size validation on FIEL upload (50KB max) - Require TLS for SMTP connections - Normalize email to lowercase before uniqueness check - Remove hardcoded default for FIEL_ENCRYPTION_KEY Also includes: - Complete production deployment documentation - API reference documentation - Security audit report with remediation details - Updated README with v0.5.0 changelog - New client admin email template - Utility scripts (create-carlos, test-emails) - PM2 ecosystem config updates Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -18,7 +18,8 @@ function getTransporter(): Transporter {
|
||||
transporter = createTransport({
|
||||
host: env.SMTP_HOST,
|
||||
port: parseInt(env.SMTP_PORT),
|
||||
secure: false, // STARTTLS
|
||||
secure: false, // Upgrade to TLS via STARTTLS
|
||||
requireTLS: true, // Reject if STARTTLS is not available
|
||||
auth: {
|
||||
user: env.SMTP_USER,
|
||||
pass: env.SMTP_PASS,
|
||||
@@ -76,4 +77,17 @@ export const emailService = {
|
||||
await sendEmail(to, 'Suscripción cancelada - Horux360', subscriptionCancelledEmail(data));
|
||||
await sendEmail(env.ADMIN_EMAIL, `Suscripción cancelada: ${data.nombre}`, subscriptionCancelledEmail(data));
|
||||
},
|
||||
|
||||
sendNewClientAdmin: async (data: {
|
||||
clienteNombre: string;
|
||||
clienteRfc: string;
|
||||
adminEmail: string;
|
||||
adminNombre: string;
|
||||
tempPassword: string;
|
||||
databaseName: string;
|
||||
plan: string;
|
||||
}) => {
|
||||
const { newClientAdminEmail } = await import('./templates/new-client-admin.js');
|
||||
await sendEmail(env.ADMIN_EMAIL, `Nuevo cliente: ${data.clienteNombre} (${data.clienteRfc})`, newClientAdminEmail(data));
|
||||
},
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user