Skip to main content

Security Fixes

Detailed guide for addressing security vulnerabilities in TrickBook.

Critical: Credential Rotation

1. MongoDB Atlas Password

  1. Log into MongoDB Atlas
  2. Go to Database Access
  3. Edit user → Edit Password
  4. Generate new secure password
  5. Update ATLAS_URI in production environment
# New connection string format
mongodb+srv://[user]:[NEW_PASSWORD]@cluster.mongodb.net/TrickList2

2. AWS Access Keys

  1. Log into AWS Console
  2. Go to IAMUsers → Your user
  3. Security credentialsCreate access key
  4. Delete old access key
  5. Update environment variables:
AWS_KEY=[your-new-aws-access-key]
AWS_SECRET=[your-new-aws-secret-key]

3. Email Password

  1. Go to email provider settings
  2. Generate new app-specific password
  3. Update EMAIL_PASSWORD in environment

4. JWT Secret

Generate cryptographically secure secret:

# Generate 32-byte random secret
openssl rand -base64 32
# Example output: <your-generated-secret-here>

Update in environment:

JWT_SECRET=<paste-your-generated-secret-here>

Critical: JWT Security

Current Issue

// INSECURE - Current code
jwt.sign(payload, "jwtPrivateKey");

Fix: Environment Variable + Expiration

// middleware/auth.js - Updated
const jwt = require('jsonwebtoken');

const JWT_SECRET = process.env.JWT_SECRET;

if (!JWT_SECRET) {
throw new Error('JWT_SECRET environment variable is required');
}

// Token generation (in auth route)
const generateToken = (user) => {
return jwt.sign(
{
_id: user._id,
name: user.name,
email: user.email,
role: user.role
},
JWT_SECRET,
{ expiresIn: '1h' } // Token expires in 1 hour
);
};

// Token verification (in middleware)
const auth = (req, res, next) => {
const token = req.header('x-auth-token');

if (!token) {
return res.status(401).json({ error: 'No token provided' });
}

try {
const decoded = jwt.verify(token, JWT_SECRET);
req.user = decoded;
next();
} catch (error) {
if (error.name === 'TokenExpiredError') {
return res.status(401).json({ error: 'Token expired' });
}
return res.status(401).json({ error: 'Invalid token' });
}
};

Mobile App: Handle Token Expiration

// app/api/client.js
import authStorage from '../auth/storage';
import jwtDecode from 'jwt-decode';

const isTokenExpired = (token) => {
try {
const decoded = jwtDecode(token);
return decoded.exp * 1000 < Date.now();
} catch {
return true;
}
};

// Check token before requests
const checkAuth = async () => {
const token = await authStorage.getToken();

if (!token || isTokenExpired(token)) {
// Redirect to login
await authStorage.removeToken();
return null;
}

return token;
};

Critical: Node.js Upgrade

Current: Node.js 12.6.x (EOL)

Target: Node.js 18 LTS or 20 LTS

Steps

  1. Update package.json:
{
"engines": {
"node": ">=18.0.0"
}
}
  1. Test locally:
# Install Node 18 via nvm
nvm install 18
nvm use 18

# Install dependencies
rm -rf node_modules package-lock.json
npm install

# Run and test
npm start
  1. Fix compatibility issues:

    • Most packages should work
    • Check for deprecated APIs
    • Update any native modules
  2. Update CI/CD:

- uses: actions/setup-node@v4
with:
node-version: '18'
  1. Update hosting:
    • Railway/Render auto-detect from package.json
    • For VPS: Install Node 18

High: Update Security Packages

helmet (v3 → v7)

npm install helmet@latest
// index.js
const helmet = require('helmet');

app.use(helmet());

// Or with custom configuration
app.use(helmet({
contentSecurityPolicy: {
directives: {
defaultSrc: ["'self'"],
styleSrc: ["'self'", "'unsafe-inline'"],
imgSrc: ["'self'", "data:", "https:"],
},
},
crossOriginEmbedderPolicy: false,
}));

jsonwebtoken (v8 → v9)

npm install jsonwebtoken@latest

No code changes needed, API is compatible.

joi (v14 → v17)

npm install joi@latest

Minor syntax changes:

// Old (v14)
const schema = Joi.object().keys({
email: Joi.string().email().required()
});

// New (v17) - same, but some methods renamed
const schema = Joi.object({
email: Joi.string().email().required()
});

High: Rate Limiting

Install

npm install express-rate-limit

Configure

// middleware/rateLimiter.js
const rateLimit = require('express-rate-limit');

// Strict limit for authentication
const authLimiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15 minutes
max: 5, // 5 requests per window
message: {
error: 'Too many login attempts. Please try again in 15 minutes.'
},
standardHeaders: true,
legacyHeaders: false,
});

// General API limit
const apiLimiter = rateLimit({
windowMs: 60 * 1000, // 1 minute
max: 100, // 100 requests per minute
message: {
error: 'Too many requests. Please slow down.'
},
});

// Strict limit for registration
const registerLimiter = rateLimit({
windowMs: 60 * 60 * 1000, // 1 hour
max: 5, // 5 registrations per hour per IP
message: {
error: 'Too many accounts created. Please try again later.'
},
});

module.exports = { authLimiter, apiLimiter, registerLimiter };

Apply

// index.js
const { authLimiter, apiLimiter, registerLimiter } = require('./middleware/rateLimiter');

// Apply to all API routes
app.use('/api/', apiLimiter);

// routes/auth.js
router.post('/', authLimiter, loginHandler);

// routes/users.js
router.post('/', registerLimiter, registerHandler);

High: CORS Whitelist

Current (Insecure)

app.use(cors()); // Allows all origins

Fixed

const corsOptions = {
origin: function (origin, callback) {
const whitelist = [
'https://thetrickbook.com',
'https://www.thetrickbook.com',
'https://admin.thetrickbook.com',
// Add localhost for development
process.env.NODE_ENV === 'development' ? 'http://localhost:3000' : null
].filter(Boolean);

if (!origin || whitelist.includes(origin)) {
callback(null, true);
} else {
callback(new Error('Not allowed by CORS'));
}
},
credentials: true,
methods: ['GET', 'POST', 'PUT', 'DELETE', 'PATCH'],
allowedHeaders: ['Content-Type', 'x-auth-token', 'Authorization']
};

app.use(cors(corsOptions));

Medium: Centralize Database Connection

Current Anti-Pattern

// Every route file does this
const client = new MongoClient(process.env.ATLAS_URI);
await client.connect();
const db = client.db('TrickList2');

Fixed: Connection Pool

// db.js
const { MongoClient } = require('mongodb');

let client = null;
let db = null;

async function connectToDatabase() {
if (db) return db;

client = new MongoClient(process.env.ATLAS_URI, {
maxPoolSize: 10,
serverSelectionTimeoutMS: 5000,
socketTimeoutMS: 45000,
});

await client.connect();
db = client.db('TrickList2');

console.log('Connected to MongoDB');
return db;
}

async function getDb() {
if (!db) {
await connectToDatabase();
}
return db;
}

async function closeConnection() {
if (client) {
await client.close();
client = null;
db = null;
}
}

module.exports = { getDb, connectToDatabase, closeConnection };

Usage in Routes

// routes/users.js
const { getDb } = require('../db');

router.get('/', async (req, res) => {
try {
const db = await getDb();
const users = await db.collection('users').find().toArray();
res.json(users);
} catch (error) {
res.status(500).json({ error: 'Database error' });
}
});

Initialize on Startup

// index.js
const { connectToDatabase } = require('./db');

const startServer = async () => {
await connectToDatabase();

app.listen(port, () => {
console.log(`Server running on port ${port}`);
});
};

startServer().catch(console.error);

Medium: Environment Validation

// config/validateEnv.js
const required = [
'ATLAS_URI',
'JWT_SECRET',
'AWS_KEY',
'AWS_SECRET',
'STRIPE_SECRET_KEY'
];

const validateEnv = () => {
const missing = required.filter(key => !process.env[key]);

if (missing.length > 0) {
console.error('Missing required environment variables:');
missing.forEach(key => console.error(` - ${key}`));
process.exit(1);
}

// Validate JWT_SECRET length
if (process.env.JWT_SECRET.length < 32) {
console.error('JWT_SECRET must be at least 32 characters');
process.exit(1);
}
};

module.exports = validateEnv;
// index.js
require('dotenv').config();
const validateEnv = require('./config/validateEnv');

validateEnv(); // Exit if missing env vars

// ... rest of app

Security Checklist

Immediate (Today)

  • Rotate MongoDB password
  • Rotate AWS access keys
  • Generate new JWT secret
  • Add .env to .gitignore

This Week

  • Update JWT to use env variable
  • Add token expiration
  • Upgrade Node.js to 18+
  • Update helmet to v7

This Sprint

  • Add rate limiting
  • Fix CORS whitelist
  • Centralize DB connection
  • Add environment validation
  • Add structured logging

Ongoing

  • Regular dependency updates (npm audit)
  • Security testing
  • Penetration testing (annual)
  • Code reviews for security