Security
Current security status and required improvements for the TrickBook backend.
This page is the July 2026 snapshot and several rows below are already fixed in code. Fixed since the September review: Apple sign-in identity is derived from the verified token (#65), avatar upload requires auth and targets the caller (#65), request bodies and identity tokens are no longer logged (#65, TrickBookWebsite #89), a /health probe exists (#65), collection validators run in warn mode (#65), and Stripe webhook deliveries verify on the raw body with idempotent handling (#75). Still open: the token check on the voice WebSocket upgrade (caps shipped 2026-09-30), the credential rotations from the September review, dependency upgrades (helmet 3, jsonwebtoken 8, joi 14), and Sentry. A rewrite of this page is tracked as the docs truth pass in Progress: September 24 to October 6.
Critical Issues
The following issues must be addressed before any further development.
1. Exposed Credentials
Status: CRITICAL
The .env file contains plaintext credentials that may be committed to git:
# EXPOSED - Rotate these immediately
ATLAS_URI=mongodb+srv://[REDACTED]@cluster.mongodb.net/...
AWS_KEY=[REDACTED]
AWS_SECRET=[REDACTED]
EMAIL_PASSWORD=[REDACTED]
Fix:
- Rotate ALL credentials immediately
- Add
.envto.gitignore - Use secrets manager (AWS Secrets Manager, Doppler, etc.)
- Create
.env.examplewith placeholder values
2. Hardcoded JWT Secret
Status: CRITICAL
// Current code - INSECURE
jwt.sign(payload, "jwtPrivateKey");
Fix:
jwt.sign(payload, process.env.JWT_SECRET, { expiresIn: '1h' });
3. No Token Expiration
Status: CRITICAL
JWT tokens never expire, meaning a leaked token grants permanent access.
Fix:
const token = jwt.sign(payload, process.env.JWT_SECRET, {
expiresIn: '1h' // Short-lived access token
});
// Implement refresh tokens for better UX
const refreshToken = jwt.sign(
{ _id: user._id },
process.env.REFRESH_SECRET,
{ expiresIn: '7d' }
);
4. End-of-Life Node.js
Status: CRITICAL
Node.js 12.6.x reached end-of-life in April 2022. No security patches available.
Fix:
// package.json
{
"engines": {
"node": ">=18.0.0"
}
}
High Priority Issues
5. Outdated helmet
Current: 3.22.0 (2019) Latest: 7.x
Missing security headers and protections.
Fix:
npm install helmet@latest
const helmet = require('helmet');
app.use(helmet());
6. No Rate Limiting
Login and API endpoints have no protection against brute force attacks.
Fix:
npm install express-rate-limit
const rateLimit = require('express-rate-limit');
// Strict limit for auth endpoints
const authLimiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15 minutes
max: 5,
message: 'Too many login attempts, try again later'
});
router.post('/auth', authLimiter, loginHandler);
// General API limit
const apiLimiter = rateLimit({
windowMs: 60 * 1000, // 1 minute
max: 100
});
app.use('/api/', apiLimiter);
7. CORS Wildcard
Current:
app.use(cors()); // Allows all origins
Fix:
const corsOptions = {
origin: [
'https://thetrickbook.com',
'https://www.thetrickbook.com',
'https://admin.thetrickbook.com'
],
credentials: true
};
app.use(cors(corsOptions));
8. Unrestricted Public Endpoints
These endpoints expose data without authentication:
| Endpoint | Risk |
|---|---|
GET /api/users/all | Exposes all user emails |
GET /api/listings/all | Exposes all user data |
GET /api/spots | May be intentional |
Fix: Add authentication or remove if not needed.
Medium Priority Issues
9. Weak Password Requirements
Current:
password: Joi.string().min(5).required()
Fix:
password: Joi.string()
.min(8)
.pattern(/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)/)
.required()
.messages({
'string.pattern.base': 'Password must contain uppercase, lowercase, and number'
})
10. No Input Sanitization
User input used directly in regex patterns:
// Vulnerable
{ name: { $regex: userInput, $options: "i" } }
Fix:
const escapeRegex = (str) => str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
{ name: { $regex: escapeRegex(userInput), $options: "i" } }
11. Missing HTTPS Redirect
Ensure all traffic uses HTTPS:
app.use((req, res, next) => {
if (req.header('x-forwarded-proto') !== 'https') {
return res.redirect(`https://${req.header('host')}${req.url}`);
}
next();
});
12. No Request Size Limits
Large payloads could cause DoS:
app.use(express.json({ limit: '10kb' }));
app.use(express.urlencoded({ limit: '10kb', extended: true }));
Security Checklist
Authentication
- Move JWT secret to environment variable
- Add token expiration (1 hour)
- Implement refresh tokens
- Add password complexity requirements
- Add account lockout after failed attempts
Authorization
- Review all public endpoints
- Add authentication to
/api/users/all - Verify admin checks on all admin routes
Network
- Update CORS whitelist
- Add rate limiting
- Enforce HTTPS
- Add request size limits
Dependencies
- Upgrade Node.js to 18+
- Update helmet to 7.x
- Update jsonwebtoken to 9.x
- Update joi to 17.x
- Run
npm auditand fix vulnerabilities
Credentials
- Rotate MongoDB password
- Rotate AWS access keys
- Rotate email password
- Create new JWT secret
- Add
.envto.gitignore - Set up secrets manager
Monitoring
- Add error logging (Winston/Pino)
- Set up Sentry for error tracking
- Monitor for suspicious activity
- Set up alerts for failed auth attempts
Secure Configuration Template
// index.js - Secure setup
const express = require('express');
const helmet = require('helmet');
const cors = require('cors');
const rateLimit = require('express-rate-limit');
const app = express();
// Security headers
app.use(helmet());
// CORS whitelist
app.use(cors({
origin: ['https://thetrickbook.com'],
credentials: true
}));
// Rate limiting
app.use(rateLimit({
windowMs: 60 * 1000,
max: 100
}));
// Request size limits
app.use(express.json({ limit: '10kb' }));
// HTTPS redirect (behind proxy)
app.use((req, res, next) => {
if (req.header('x-forwarded-proto') !== 'https') {
return res.redirect(301, `https://${req.header('host')}${req.url}`);
}
next();
});
// Validate required env vars
const required = ['ATLAS_URI', 'JWT_SECRET', 'AWS_KEY'];
required.forEach(key => {
if (!process.env[key]) {
console.error(`Missing required env var: ${key}`);
process.exit(1);
}
});