Security Fixes
Detailed guide for addressing security vulnerabilities in TrickBook.
Critical: Credential Rotation
1. MongoDB Atlas Password
- Log into MongoDB Atlas
- Go to Database Access
- Edit user → Edit Password
- Generate new secure password
- Update
ATLAS_URIin production environment
# New connection string format
mongodb+srv://[user]:[NEW_PASSWORD]@cluster.mongodb.net/TrickList2
2. AWS Access Keys
- Log into AWS Console
- Go to IAM → Users → Your user
- Security credentials → Create access key
- Delete old access key
- Update environment variables:
AWS_KEY=[your-new-aws-access-key]
AWS_SECRET=[your-new-aws-secret-key]
3. Email Password
- Go to email provider settings
- Generate new app-specific password
- Update
EMAIL_PASSWORDin 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
- Update package.json:
{
"engines": {
"node": ">=18.0.0"
}
}
- 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
-
Fix compatibility issues:
- Most packages should work
- Check for deprecated APIs
- Update any native modules
-
Update CI/CD:
- uses: actions/setup-node@v4
with:
node-version: '18'
- 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
.envto.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