T05 · Unauthorized Access and Privilege Escalation
Error
- Location
- src/services/chatService.ts:10
- Finding
- Unauthenticated Chat Access and User Impersonation## Vulnerability Details **File Location**: `src/services/chatService.ts:10-59`, `src/services/chatService.ts:111-118`, and `src/services/chatService.ts:146-169` **Vulnerability Type**: Missing authentication and room-level authorization **Risk Level**: High ### Vulnerable Code ```ts static sendMessage( roomId: number, userId: string, userName: string, content: string, messageType: MessageType = MessageType.TEXT ): ChatMessage { const db = getDatabase(); const result = db.prepare(` INSERT INTO chat_messages (room_id, user_id, user_name, content, message_type) VALUES (?, ?, ?, ?, ?) `).run(roomId, userId, userName, content, messageType); const message = this.getById(result.lastInsertRowid as number)!; chatEvents.emit('message', { roomId, message }); return message; } ``` ```ts static getRoomMessages( roomId: number, limit: number = 100, beforeId?: number ): ChatMessage[] { const db = getDatabase(); let sql = 'SELECT * FROM chat_messages WHERE room_id = ?'; const params: any[] = [roomId]; if (beforeId) { sql += ' AND id < ?'; params.push(beforeId); } sql += ' ORDER BY created_at DESC LIMIT ?'; params.push(limit); const rows = db.prepare(sql).all(...params) as any[]; return rows.map(this.mapRowToMessage).reverse(); } ``` ```ts static deleteMessage(messageId: number, userId: string): boolean { const db = getDatabase(); const message = this.getById(messageId); if (!message) throw new Error('Message does not exist'); if (message.userId !== userId) throw new Error('Users may only delete their own messages'); const result = db.prepare('DELETE FROM chat_messages WHERE id = ?').run(messageId); return result.changes > 0; } ``` ```ts static exportChat(roomId: number): string { const messages = this.getRoomMessages(roomId, 10000); let markdow ...[truncated 2712 chars]
- Remediation
- ## Remediation Suggestions 1. Introduce an authenticated principal derived from a verified session or signed token. Do not accept the acting user ID as an authoritative request parameter. 2. Change service methods to receive a trusted authentication context, such as `AuthContext`, and derive `userId` from that context. 3. Before every chat read, search, statistics, or export operation, verify that the authenticated user belongs to the requested room. 4. Before sending a message, verify that the room exists, that the authenticated user is a member, and that the room status permits messaging. 5. Load the display name from the registered user record instead of accepting `userName` from the caller. 6. For deletion, compare the message owner with the authenticated principal rather than a supplied `userId`. Define explicit moderator or host permissions if privileged deletion is required. 7. Apply pagination and bounded limits to exports and history operations to reduce denial-of-service exposure. 8. Centralize room authorization in a reusable guard so new chat methods cannot accidentally omit membership checks. 9. Add negative tests covering non-members, spoofed identities, unauthorized exports, and deletion attempts using another user's ID.
