There is Signaling Server (in Node.js) that coordinates setup of WebRTC connection between two clients.
We have implemented it in span of two milestones, where priority was fast delivery of the working product.
To ensure longterm reliability of the server, few improvements should be implemented.
Make Signaling server more robust and secure.
Claude 4 Code Review full details
🔴 Critical Memory Leaks
1. MatchedSignalingConnectionsProvider - Memory Leak in ID Management
export class MatchedSignalingConnectionsProvider {
private connections: WeakMap<{ id: string }, SignalingConnection> =
new WeakMap();
private ids: Set<{ id: string }> = new Set();
Issue: The ids Set holds strong references to objects, preventing garbage collection even after connections are removed. The WeakMap becomes ineffective.
Fix: Use a Map instead of WeakMap + Set combination:
private connections: Map<string, SignalingConnection> = new Map();
2. SignalingConnection - Circular Reference Memory Leak
private _state:
| undefined
| { mode: "acceptsOffers"; offerSender?: SignalingConnection }
| { mode: "sendsOffer"; matchedAcceptor: SignalingConnection };
Issue: SignalingConnections hold references to each other. When one closes, it doesn't clean up references in its paired connection.
Fix: Implement proper cleanup:
close() {
if (this.state?.mode === "acceptsOffers" && this.state.offerSender) {
// Notify the offer sender to clean up its reference
this.state.offerSender.cleanupMatchedConnection();
} else if (this.state?.mode === "sendsOffer") {
// Notify the acceptor to clean up its reference
this.state.matchedAcceptor.cleanupMatchedConnection();
}
this.state = undefined;
}
private cleanupMatchedConnection() {
this.state = undefined;
}
3. WebSocketJSONHandler - Unbounded Listener Arrays
private listeners: {
onClose: ((code: number, reason: Buffer) => void)[];
onError: ((error: Error) => void)[];
onMessage: ((arg: object) => void)[];
onSpecificMessage: {
condition: (arg: object) => boolean;
fn: (arg: object) => void;
}[];
} = {
Issue: No way to remove listeners, arrays can grow indefinitely.
Fix: Add cleanup method and return unsubscribe functions:
public onSpecificMessage<T extends object>(
condition: (payload: object) => payload is T,
fn: (payload: T) => void,
): () => void {
const listener = { condition, fn: fn as (payload: object) => void };
this.listeners.onSpecificMessage.push(listener);
// Return unsubscribe function
return () => {
const index = this.listeners.onSpecificMessage.indexOf(listener);
if (index > -1) {
this.listeners.onSpecificMessage.splice(index, 1);
}
};
}
🔴 Critical Security Vulnerabilities
1. No Authentication or Authorization
console.warn(payload.sessionToken, "sessionToken is ignored for now");
Issue: Anyone can connect and establish WebRTC connections through your server.
Fix: Implement proper authentication:
// Add JWT or API key validation
private async validateSessionToken(token: string): Promise<boolean> {
// Implement token validation
return await tokenService.validate(token);
}
2. Denial of Service (DoS) Vulnerabilities
Issues:
- No connection limits
- No rate limiting
- No message size limits
- No idle connection timeout
Fix: Add protection mechanisms:
class SignalingServer {
private readonly MAX_CONNECTIONS = 1000;
private readonly MAX_MESSAGE_SIZE = 64 * 1024; // 64KB
private readonly CONNECTION_TIMEOUT = 60000; // 60 seconds
private setupWebSocketHandlers(): void {
this.wss.on("connection", (ws: WebSocket, request) => {
if (this.signalingConnections.length >= this.MAX_CONNECTIONS) {
ws.close(1008, "Server at capacity");
return;
}
// Set message size limit
ws.setMaxPayloadSize(this.MAX_MESSAGE_SIZE);
// Set idle timeout
const timeout = setTimeout(() => {
ws.close(1000, "Idle timeout");
}, this.CONNECTION_TIMEOUT);
// Reset timeout on activity
ws.on("message", () => {
clearTimeout(timeout);
setTimeout(...);
});
});
}
}
3. Input Validation Issues
offer: unknown;
iceCandidates: unknown[];
Issue: No validation of WebRTC offer/answer content, could lead to injection attacks.
Fix: Add proper validation:
function validateOffer(offer: unknown): boolean {
if (!offer || typeof offer !== 'object') return false;
// Add SDP validation
if ('sdp' in offer && typeof offer.sdp === 'string') {
// Validate SDP format and content
return isValidSDP(offer.sdp);
}
return false;
}
4. Information Disclosure
console.log(
`[SignalingServer] New signaling connection: ${signalingConnection.localID}; Total connections: ${this.signalingConnections.length}`,
);
Issue: Logs expose internal state and connection information.
Fix: Use structured logging with appropriate levels:
logger.debug('New connection established', {
connectionId: hash(signalingConnection.localID),
timestamp: new Date().toISOString()
});
🟡 Additional Security Recommendations
- Add CORS/Origin Validation:
this.wss.on("connection", (ws: WebSocket, request) => {
const origin = request.headers.origin;
if (!isAllowedOrigin(origin)) {
ws.close(1008, "Origin not allowed");
return;
}
});
- Enforce Secure Connections:
// Only accept WSS connections in production
if (process.env.NODE_ENV === 'production' && !request.secure) {
ws.close(1008, "Secure connection required");
return;
}
- Add Connection Metadata Tracking:
interface ConnectionMetadata {
id: string;
connectedAt: Date;
lastActivity: Date;
messageCount: number;
ipAddress: string;
}
- Implement Proper Error Handling:
try {
// Handle message
} catch (error) {
logger.error('Message handling failed', { error: error.message });
ws.close(1011, "Internal server error");
}
🟢 Positive Aspects
- Good TypeScript usage with type guards
- Clean separation of concerns
- Proper use of event-driven architecture
- Good code organization
Summary
The most critical issues to address immediately are:
- Fix the memory leak in
MatchedSignalingConnectionsProvider
- Implement proper cleanup for circular references in
SignalingConnection
- Add authentication/authorization
- Implement rate limiting and connection limits
- Validate all user inputs properly
These fixes will significantly improve both the reliability and security of your signaling server.
Context
There is Signaling Server (in Node.js) that coordinates setup of WebRTC connection between two clients.
We have implemented it in span of two milestones, where priority was fast delivery of the working product.
Problem
To ensure longterm reliability of the server, few improvements should be implemented.
What
Make Signaling server more robust and secure.
What exactly ?
MatchedSignalingConnectionsProvider,SignalingConnection,WebSocketJSONHandlerFor more details look at Claude 4 Code Review below
Code Review: Signaling Server
Claude 4 Code Review full details
🔴 Critical Memory Leaks
1. MatchedSignalingConnectionsProvider - Memory Leak in ID Management
Issue: The
idsSet holds strong references to objects, preventing garbage collection even after connections are removed. The WeakMap becomes ineffective.Fix: Use a Map instead of WeakMap + Set combination:
2. SignalingConnection - Circular Reference Memory Leak
Issue: SignalingConnections hold references to each other. When one closes, it doesn't clean up references in its paired connection.
Fix: Implement proper cleanup:
3. WebSocketJSONHandler - Unbounded Listener Arrays
Issue: No way to remove listeners, arrays can grow indefinitely.
Fix: Add cleanup method and return unsubscribe functions:
🔴 Critical Security Vulnerabilities
1. No Authentication or Authorization
Issue: Anyone can connect and establish WebRTC connections through your server.
Fix: Implement proper authentication:
2. Denial of Service (DoS) Vulnerabilities
Issues:
Fix: Add protection mechanisms:
3. Input Validation Issues
Issue: No validation of WebRTC offer/answer content, could lead to injection attacks.
Fix: Add proper validation:
4. Information Disclosure
Issue: Logs expose internal state and connection information.
Fix: Use structured logging with appropriate levels:
🟡 Additional Security Recommendations
🟢 Positive Aspects
Summary
The most critical issues to address immediately are:
MatchedSignalingConnectionsProviderSignalingConnectionThese fixes will significantly improve both the reliability and security of your signaling server.