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:
Claude 2026-01-19 06:38:10 +00:00
parent 41b03b88d5
commit 13d926a9c5
No known key found for this signature in database
4 changed files with 44 additions and 12 deletions

View file

@ -7,21 +7,28 @@ const jwt = require('jsonwebtoken');
function authenticateUser(req, res, next) {
try {
// 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
const authHeader = req.headers.authorization;
if (!authHeader || !authHeader.startsWith('Bearer ')) {
// Use demo user for development
console.warn('⚠️ Development mode: Using demo user without authentication');
req.user = {
id: 'demo-user-123',
email: 'demo@aethex.dev'
};
return next();
}
const token = authHeader.substring(7);
// Try to verify, but don't fail if invalid
try {
const decoded = jwt.verify(token, process.env.JWT_SECRET);
@ -31,15 +38,16 @@ function authenticateUser(req, res, next) {
};
} catch {
// Use demo user if token invalid
console.warn('⚠️ Development mode: Invalid token, using demo user');
req.user = {
id: 'demo-user-123',
email: 'demo@aethex.dev'
};
}
return next();
}
// Production: Strict auth required
const authHeader = req.headers.authorization;

View file

@ -29,6 +29,10 @@ class CallService {
[conversationId]
);
if (conversationResult.rows.length === 0) {
throw new Error(`Conversation ${conversationId} not found`);
}
const conversation = conversationResult.rows[0];
// Determine if group call
@ -249,9 +253,12 @@ class CallService {
async generateTURNCredentials(userId) {
const timestamp = Math.floor(Date.now() / 1000) + 86400; // 24 hour TTL
const username = `${timestamp}:${userId}`;
// 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);
hmac.update(username);
const credential = hmac.digest('base64');
@ -265,8 +272,12 @@ class CallService {
[userId, username, credential, timestamp]
);
const turnHost = process.env.TURN_SERVER_HOST || 'turn.aethex.app';
const turnPort = process.env.TURN_SERVER_PORT || '3478';
const turnHost = process.env.TURN_SERVER_HOST;
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 {
urls: [

View file

@ -36,6 +36,10 @@ class GameForgeIntegrationService {
]
);
if (integrationResult.rows.length === 0) {
throw new Error('Failed to create GameForge integration');
}
const integration = integrationResult.rows[0];
// Create channels
@ -88,13 +92,17 @@ class GameForgeIntegrationService {
const description = this.getChannelDescription(channelName);
const conversationResult = await db.query(
`INSERT INTO conversations
`INSERT INTO conversations
(type, title, description, created_by, gameforge_project_id)
VALUES ('group', $1, $2, $3, $4)
RETURNING *`,
[title, description, ownerId, projectId]
);
if (conversationResult.rows.length === 0) {
throw new Error('Failed to create conversation for GameForge channel');
}
const conversation = conversationResult.rows[0];
// Add team members based on permissions

View file

@ -287,8 +287,13 @@ class MessagingService {
`SELECT username, verified_domain, avatar_url FROM users WHERE id = $1`,
[userId]
);
if (userResult.rows.length === 0) {
throw new Error(`User ${userId} not found`);
}
const user = userResult.rows[0];
return {
id: message.id,
conversationId: message.conversation_id,