Fix critical security vulnerabilities and array access bugs
This commit addresses multiple high-priority security and reliability issues
found during comprehensive codebase analysis:
1. CRITICAL: Remove hardcoded TURN server credentials
- Removed dangerous default secret 'default-secret-change-me'
- Now throws error if TURN_SECRET env var not set
- Also requires TURN_SERVER_HOST and TURN_SERVER_PORT to be configured
- Prevents attackers from generating valid TURN credentials
- File: src/backend/services/callService.js
2. HIGH: Add array bounds checking before accessing database results
- Added validation before accessing rows[0] in multiple locations
- Prevents "Cannot read property of undefined" runtime crashes
- Provides clear error messages when records not found
- Files affected:
- src/backend/services/callService.js (conversation lookup)
- src/backend/services/messagingService.js (user lookup)
- src/backend/services/gameforgeIntegration.js (integration & conversation creation)
3. HIGH: Secure development mode authentication bypass
- Added second security layer requiring ALLOW_DEV_BYPASS='true'
- Prevents accidental auth bypass if NODE_ENV accidentally set to 'development'
- Added warning logs when bypass is active
- File: src/backend/middleware/auth.js
These fixes prevent:
- TURN server abuse via known secrets
- Runtime crashes from missing database records
- Accidental authentication bypass in production environments
This commit is contained in:
parent
41b03b88d5
commit
13d926a9c5
4 changed files with 44 additions and 12 deletions
|
|
@ -7,12 +7,19 @@ const jwt = require('jsonwebtoken');
|
||||||
function authenticateUser(req, res, next) {
|
function authenticateUser(req, res, next) {
|
||||||
try {
|
try {
|
||||||
// In development mode, allow requests without auth for testing
|
// In development mode, allow requests without auth for testing
|
||||||
if (process.env.NODE_ENV === 'development') {
|
// SECURITY: This bypass only works if BOTH conditions are met:
|
||||||
|
// 1. NODE_ENV is 'development'
|
||||||
|
// 2. ALLOW_DEV_BYPASS is explicitly set to 'true'
|
||||||
|
const isDevelopment = process.env.NODE_ENV === 'development';
|
||||||
|
const allowDevBypass = process.env.ALLOW_DEV_BYPASS === 'true';
|
||||||
|
|
||||||
|
if (isDevelopment && allowDevBypass) {
|
||||||
// Check for token, but if not present, use demo user
|
// Check for token, but if not present, use demo user
|
||||||
const authHeader = req.headers.authorization;
|
const authHeader = req.headers.authorization;
|
||||||
|
|
||||||
if (!authHeader || !authHeader.startsWith('Bearer ')) {
|
if (!authHeader || !authHeader.startsWith('Bearer ')) {
|
||||||
// Use demo user for development
|
// Use demo user for development
|
||||||
|
console.warn('⚠️ Development mode: Using demo user without authentication');
|
||||||
req.user = {
|
req.user = {
|
||||||
id: 'demo-user-123',
|
id: 'demo-user-123',
|
||||||
email: 'demo@aethex.dev'
|
email: 'demo@aethex.dev'
|
||||||
|
|
@ -31,6 +38,7 @@ function authenticateUser(req, res, next) {
|
||||||
};
|
};
|
||||||
} catch {
|
} catch {
|
||||||
// Use demo user if token invalid
|
// Use demo user if token invalid
|
||||||
|
console.warn('⚠️ Development mode: Invalid token, using demo user');
|
||||||
req.user = {
|
req.user = {
|
||||||
id: 'demo-user-123',
|
id: 'demo-user-123',
|
||||||
email: 'demo@aethex.dev'
|
email: 'demo@aethex.dev'
|
||||||
|
|
|
||||||
|
|
@ -29,6 +29,10 @@ class CallService {
|
||||||
[conversationId]
|
[conversationId]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if (conversationResult.rows.length === 0) {
|
||||||
|
throw new Error(`Conversation ${conversationId} not found`);
|
||||||
|
}
|
||||||
|
|
||||||
const conversation = conversationResult.rows[0];
|
const conversation = conversationResult.rows[0];
|
||||||
|
|
||||||
// Determine if group call
|
// Determine if group call
|
||||||
|
|
@ -251,7 +255,10 @@ class CallService {
|
||||||
const username = `${timestamp}:${userId}`;
|
const username = `${timestamp}:${userId}`;
|
||||||
|
|
||||||
// Generate credential using HMAC
|
// Generate credential using HMAC
|
||||||
const turnSecret = process.env.TURN_SECRET || 'default-secret-change-me';
|
const turnSecret = process.env.TURN_SECRET;
|
||||||
|
if (!turnSecret) {
|
||||||
|
throw new Error('TURN_SECRET environment variable must be set for TURN server authentication');
|
||||||
|
}
|
||||||
const hmac = crypto.createHmac('sha1', turnSecret);
|
const hmac = crypto.createHmac('sha1', turnSecret);
|
||||||
hmac.update(username);
|
hmac.update(username);
|
||||||
const credential = hmac.digest('base64');
|
const credential = hmac.digest('base64');
|
||||||
|
|
@ -265,8 +272,12 @@ class CallService {
|
||||||
[userId, username, credential, timestamp]
|
[userId, username, credential, timestamp]
|
||||||
);
|
);
|
||||||
|
|
||||||
const turnHost = process.env.TURN_SERVER_HOST || 'turn.aethex.app';
|
const turnHost = process.env.TURN_SERVER_HOST;
|
||||||
const turnPort = process.env.TURN_SERVER_PORT || '3478';
|
const turnPort = process.env.TURN_SERVER_PORT;
|
||||||
|
|
||||||
|
if (!turnHost || !turnPort) {
|
||||||
|
throw new Error('TURN_SERVER_HOST and TURN_SERVER_PORT environment variables must be set');
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
urls: [
|
urls: [
|
||||||
|
|
|
||||||
|
|
@ -36,6 +36,10 @@ class GameForgeIntegrationService {
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if (integrationResult.rows.length === 0) {
|
||||||
|
throw new Error('Failed to create GameForge integration');
|
||||||
|
}
|
||||||
|
|
||||||
const integration = integrationResult.rows[0];
|
const integration = integrationResult.rows[0];
|
||||||
|
|
||||||
// Create channels
|
// Create channels
|
||||||
|
|
@ -95,6 +99,10 @@ class GameForgeIntegrationService {
|
||||||
[title, description, ownerId, projectId]
|
[title, description, ownerId, projectId]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if (conversationResult.rows.length === 0) {
|
||||||
|
throw new Error('Failed to create conversation for GameForge channel');
|
||||||
|
}
|
||||||
|
|
||||||
const conversation = conversationResult.rows[0];
|
const conversation = conversationResult.rows[0];
|
||||||
|
|
||||||
// Add team members based on permissions
|
// Add team members based on permissions
|
||||||
|
|
|
||||||
|
|
@ -287,6 +287,11 @@ class MessagingService {
|
||||||
`SELECT username, verified_domain, avatar_url FROM users WHERE id = $1`,
|
`SELECT username, verified_domain, avatar_url FROM users WHERE id = $1`,
|
||||||
[userId]
|
[userId]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if (userResult.rows.length === 0) {
|
||||||
|
throw new Error(`User ${userId} not found`);
|
||||||
|
}
|
||||||
|
|
||||||
const user = userResult.rows[0];
|
const user = userResult.rows[0];
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue