Security is paramount in real-time communications. WebRTC provides built-in security features, but proper implementation and additional measures are necessary to protect user privacy and prevent attacks. This guide covers WebRTC security best practices for developers.
Built-in WebRTC Security
WebRTC includes strong security by default:
- Mandatory Encryption: All media and data streams are encrypted using DTLS (Datagram Transport Layer Security) for key exchange and SRTP (Secure Real-Time Transport Protocol) for media.
- Secure Contexts: Modern browsers only allow WebRTC in secure contexts (HTTPS pages), preventing man-in-the-middle attacks.
- Permission Models: Users must explicitly grant camera and microphone access, protecting against unauthorized surveillance.
- Perfect Forward Secrecy: Each session uses unique encryption keys that can't be derived from previous sessions.
Authentication and Authorization
Implement robust authentication for your WebRTC application:
Signaling Authentication
Secure the signaling channel with token-based authentication:
// Server generates JWT token
const jwt = require('jsonwebtoken');
function generateToken(userId) {
return jwt.sign(
{ userId, exp: Math.floor(Date.now() / 1000) + 3600 },
process.env.JWT_SECRET
);
}
// Client includes token in signaling
const ws = new WebSocket(`wss://signal.example.com?token=${token}`);
TURN Server Authentication
Use time-limited credentials for TURN servers:
function generateTurnCredentials(username, secret) {
const timestamp = Math.floor(Date.now() / 1000) + 86400; // 24 hours
const turnUsername = `${timestamp}:${username}`;
const hmac = crypto.createHmac('sha1', secret);
hmac.update(turnUsername);
const credential = hmac.digest('base64');
return { username: turnUsername, credential };
}
Network Security
Protect against network-level attacks:
Isolate TURN Traffic
Deploy TURN servers in isolated network segments:
- Separate TURN servers from application servers.
- Use dedicated IP ranges.
- Implement firewall rules restricting access.
Rate Limiting
Prevent resource exhaustion:
const rateLimit = require('express-rate-limit');
const signalingLimiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15 minutes
max: 100, // Limit each IP to 100 requests per windowMs
message: 'Too many signaling requests'
});
app.use('/signaling', signalingLimiter);
DDoS Protection
Implement DDoS mitigation:
- Use CDN services like Cloudflare.
- Deploy load balancers with DDoS protection.
- Implement connection throttling.
- Monitor traffic patterns.
Media Security
Ensure media streams remain private:
End-to-End Encryption
For sensitive communications, implement additional encryption:
// Insertable Streams API for additional encryption
const sender = pc.addTrack(mediaStreamTrack);
const senderStreams = sender.createEncodedStreams();
senderStreams.readable
.pipeThrough(new TransformStream({
transform: encryptFrame
}))
.pipeTo(senderStreams.writable);
function encryptFrame(chunk, controller) {
// Additional encryption logic
const encrypted = customEncrypt(chunk.data);
chunk.data = encrypted;
controller.enqueue(chunk);
}
Screen Sharing Protection
Protect screen sharing from information disclosure:
async function getScreenShare() {
const stream = await navigator.mediaDevices.getDisplayMedia({
video: {
cursor: 'never' // Don't show cursor
},
audio: false,
surfaceSwitching: 'exclude' // Prevent window switching
});
// Monitor for window changes
stream.getVideoTracks()[0].addEventListener('ended', () => {
console.log('Screen sharing ended');
cleanup();
});
return stream;
}
Privacy Protection
Respect user privacy:
IP Address Leakage Prevention
WebRTC can leak IP addresses even when using VPNs. Mitigate this:
const config = {
iceServers: [...],
iceTransportPolicy: 'relay' // Force TURN for maximum privacy
};
const pc = new RTCPeerConnection(config);
Minimal Data Collection
Only collect necessary data:
- Don't log media content.
- Minimize metadata collection.
- Implement data retention policies.
- Provide clear privacy policies.
Consent Management
Ensure proper user consent:
async function requestMediaPermissions() {
try {
const stream = await navigator.mediaDevices.getUserMedia({
video: true,
audio: true
});
// Show clear indication that media is active
showMediaActiveIndicator();
return stream;
} catch (error) {
if (error.name === 'NotAllowedError') {
showPermissionDeniedMessage();
}
throw error;
}
}
Signaling Channel Security
Secure the signaling channel against attacks:
Message Validation
Validate all signaling messages:
function validateSignalingMessage(message) {
const schema = {
type: ['offer', 'answer', 'ice-candidate', 'bye'],
timestamp: 'number',
from: 'string',
to: 'string'
};
// Validate message structure
if (!validateSchema(message, schema)) {
throw new Error('Invalid message format');
}
// Validate timestamp to prevent replay attacks
const messageAge = Date.now() - message.timestamp;
if (messageAge > 60000) { // 60 seconds
throw new Error('Message too old');
}
// Validate SDP if present
if (message.sdp) {
validateSDP(message.sdp);
}
return true;
}
Prevent Injection Attacks
Sanitize all user inputs:
function sanitizeUserInput(input) {
return input
.replace(/[<>'"]/g, '') // Remove potential XSS characters
.trim()
.substring(0, 100); // Limit length
}
function createRoom(roomName) {
const sanitized = sanitizeUserInput(roomName);
if (!/^[a-zA-Z0-9-_]+$/.test(sanitized)) {
throw new Error('Invalid room name');
}
return sanitized;
}
TURN Server Security
Secure your TURN infrastructure:
Credential Rotation
Regularly rotate TURN credentials:
// Implement credential rotation
setInterval(() => {
const newSecret = generateSecret();
updateTurnServerSecret(newSecret);
notifyApplicationServers(newSecret);
}, 7 * 24 * 60 * 60 * 1000); // Weekly rotation
Access Control
Restrict TURN server access:
# /etc/turnserver.conf
# Restrict IP ranges that can use relay
allowed-peer-ip=10.0.0.0-10.255.255.255
allowed-peer-ip=172.16.0.0-172.31.255.255
# Deny potentially dangerous IPs
denied-peer-ip=0.0.0.0-0.255.255.255
denied-peer-ip=127.0.0.0-127.255.255.255
After tightening these rules, confirm legitimate clients can still allocate a relay using the free ICE server tester.
Usage Monitoring
Monitor TURN usage for anomalies:
function monitorTurnUsage() {
const metrics = {
activeAllocations: getTurnAllocations(),
bandwidth: getTurnBandwidth(),
failedAuth: getFailedAuthCount()
};
// Alert on suspicious patterns
if (metrics.failedAuth > THRESHOLD) {
alertSecurityTeam('Possible TURN brute force attack');
}
if (metrics.bandwidth > BANDWIDTH_LIMIT) {
alertSecurityTeam('Unusual TURN bandwidth usage');
}
}
Compliance Considerations
Ensure compliance with relevant regulations:
GDPR Compliance
For European users:
- Obtain explicit consent for data processing.
- Implement data portability.
- Provide deletion capabilities.
- Document data processing activities.
- Appoint a DPO if required.
HIPAA Compliance
For healthcare applications:
- Use Business Associate Agreements.
- Implement audit logging.
- Encrypt all data in transit and at rest.
- Run regular security assessments.
- Maintain incident response procedures.
Recording Consent
When recording sessions:
async function startRecording(participants) {
// Obtain consent from all participants
const consents = await Promise.all(
participants.map(p => requestRecordingConsent(p))
);
if (!consents.every(c => c === true)) {
throw new Error('Not all participants consented to recording');
}
// Show clear recording indicator
showRecordingIndicator();
// Start recording with audit trail
logRecordingStart(participants, consents);
}
Security Monitoring
Implement comprehensive monitoring:
Logging
Log security-relevant events:
function logSecurityEvent(event) {
const logEntry = {
timestamp: Date.now(),
type: event.type,
userId: event.userId,
ip: event.ip,
details: event.details,
severity: event.severity
};
// Send to security information and event management (SIEM)
securityLogger.log(logEntry);
// Alert on high-severity events
if (event.severity === 'high') {
alertSecurityTeam(logEntry);
}
}
Intrusion Detection
Monitor for suspicious activity:
function detectAnomalies(userActivity) {
const patterns = {
rapidConnections: countConnections(userActivity, 60000) > 10,
multipleLocations: detectLocationChanges(userActivity),
unusualTiming: detectOffHoursAccess(userActivity),
highFailureRate: calculateFailureRate(userActivity) > 0.5
};
if (Object.values(patterns).some(p => p)) {
flagSuspiciousActivity(userActivity);
}
}
Incident Response
Prepare for security incidents:
Response Plan
- Detection: Identify the incident.
- Containment: Isolate affected systems.
- Investigation: Determine scope and cause.
- Remediation: Fix vulnerabilities.
- Recovery: Restore normal operations.
- Post-Incident: Review and improve.
Emergency Procedures
async function handleSecurityIncident(incident) {
// Immediate containment
await isolateAffectedServers(incident.affectedServers);
// Notify stakeholders
await notifySecurityTeam(incident);
await notifyAffectedUsers(incident);
// Begin investigation
const forensics = await collectForensicData(incident);
// Implement fixes
await deploySecurityPatches();
// Monitor for recurrence
await enhanceMonitoring(incident.type);
// Document incident
await createIncidentReport(incident, forensics);
}
Security Checklist
Use this checklist for security audits:
- HTTPS/WSS for all communications
- Authentication on all endpoints
- Time-limited TURN credentials
- Rate limiting implemented
- Input validation and sanitization
- Security headers configured
- Logging and monitoring active
- Incident response plan documented
- Regular security assessments
- Compliance requirements met
- User consent mechanisms
- Data encryption at rest and in transit
- Access control properly configured
- Regular security training for the team
Conclusion
WebRTC security requires a multi-layered approach combining the protocol's built-in security features with application-level protections, network security, and operational practices. By implementing these best practices, you can build WebRTC applications that protect user privacy, prevent attacks, and maintain trust.
Remember that security is not a one-time task but an ongoing process. Regularly review and update your security measures, stay informed about new vulnerabilities, and maintain vigilance against emerging threats.
Check Your Own Servers
Locked-down TURN credentials and access rules are only safe if they still work. After hardening, run your
turn:/turns: servers through the tester to confirm authentication succeeds and no
relay is left open to anonymous use.