Security best practices
Security best practices
Section titled “Security best practices”Security is crucial when building real-time applications. This guide covers best practices for securing your Jetsocket.io applications.
Authentication and Authorization
Section titled “Authentication and Authorization”Always authenticate users
Section titled “Always authenticate users”Never allow unauthorized access to private or presence channels:
// Good: Always verify user identityapp.post("/jetsocket/auth", authenticateUser, (req, res) => { const user = req.user; const channel = req.body.channel_name;
if (canAccessChannel(user, channel)) { const authResponse = jetsocket.authorizeChannel(req.body.socket_id, channel); res.send(authResponse); } else { res.status(403).send("Forbidden"); }});
// Bad: No authenticationapp.post("/jetsocket/auth", (req, res) => { const authResponse = jetsocket.authorizeChannel(req.body.socket_id, req.body.channel_name); res.send(authResponse);});Validate channel access
Section titled “Validate channel access”Implement proper channel access control:
function canAccessChannel(user, channel) { // Validate channel name format if (!channel || typeof channel !== 'string') { return false; }
// Check for private channel prefix if (channel.startsWith('private-')) { // Extract user ID from channel name if (channel.startsWith('private-user-')) { const userId = channel.replace('private-user-', ''); return user.id === userId; }
// Check role-based access if (channel === 'private-admin-dashboard') { return user.role === 'admin'; }
// Check group-based access if (channel.startsWith('private-group-')) { const groupId = channel.replace('private-group-', ''); return user.groups.includes(groupId); } }
return false;}Use HTTPS
Section titled “Use HTTPS”Always use HTTPS for your authorization endpoints:
// Good: HTTPS endpointconst jetsocket = new Jetsocket("APP_KEY", { cluster: "APP_CLUSTER", channelAuthorization: { endpoint: "https://yourdomain.com/jetsocket/auth" }});
// Bad: HTTP endpoint (insecure)const jetsocket = new Jetsocket("APP_KEY", { cluster: "APP_CLUSTER", channelAuthorization: { endpoint: "http://yourdomain.com/jetsocket/auth" }});Channel Security
Section titled “Channel Security”Channel naming conventions
Section titled “Channel naming conventions”Use descriptive and secure channel names:
// Good channel names"private-user-123""private-group-456""presence-chat-room""private-encrypted-financial-data"
// Bad channel names"private-secret""private-12345""private-admin" // Too genericValidate channel names
Section titled “Validate channel names”Always validate channel names on the server:
function validateChannelName(channel) { // Check for required prefixes if (channel.startsWith('private-') || channel.startsWith('presence-')) { // Validate format const pattern = /^[a-zA-Z0-9_-]+$/; return pattern.test(channel.replace(/^(private-|presence-)/, '')); }
// Public channels return /^[a-zA-Z0-9_-]+$/.test(channel);}
app.post("/jetsocket/auth", (req, res) => { const channel = req.body.channel_name;
if (!validateChannelName(channel)) { return res.status(400).send("Invalid channel name"); }
// Continue with authorization...});Rate limiting
Section titled “Rate limiting”Implement rate limiting on your authorization endpoints:
const rateLimit = require("express-rate-limit");
const authLimiter = rateLimit({ windowMs: 15 * 60 * 1000, // 15 minutes max: 100, // limit each IP to 100 requests per windowMs message: "Too many authorization requests"});
app.post("/jetsocket/auth", authLimiter, authenticateUser, (req, res) => { // Authorization logic});Data Security
Section titled “Data Security”Don’t send sensitive data on public channels
Section titled “Don’t send sensitive data on public channels”// Good: Use private channels for sensitive datajetsocket.trigger("private-user-123", "personal-notification", { message: "Your account has been updated"});
// Bad: Sending sensitive data on public channelsjetsocket.trigger("notifications", "user-update", { user_id: "123", email: "user@example.com", password_hash: "abc123" // Never do this!});Validate all data
Section titled “Validate all data”Always validate data on the server side:
app.post("/send-message", authenticateUser, (req, res) => { const { message, channel } = req.body;
// Validate message if (!message || typeof message !== 'string' || message.length > 1000) { return res.status(400).json({ error: "Invalid message" }); }
// Validate channel if (!validateChannelName(channel)) { return res.status(400).json({ error: "Invalid channel" }); }
// Sanitize message const sanitizedMessage = sanitizeInput(message);
// Send message jetsocket.trigger(channel, "new-message", { message: sanitizedMessage, user_id: req.user.id });
res.json({ success: true });});
function sanitizeInput(input) { // Remove potentially dangerous content return input .replace(/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi, '') .replace(/javascript:/gi, '') .trim();}Use encrypted channels for sensitive data
Section titled “Use encrypted channels for sensitive data”// For financial, medical, or legal dataconst encryptedChannel = jetsocket.subscribe("private-encrypted-financial-data");
// Server-sideconst jetsocket = new Jetsocket({ appId: "APP_ID", key: "APP_KEY", secret: "APP_SECRET", cluster: "APP_CLUSTER", encryptionMasterKey: process.env.ENCRYPTION_MASTER_KEY});API Security
Section titled “API Security”Secure your API keys
Section titled “Secure your API keys”Never expose your API keys in client-side code:
// Good: Keys only on serverconst jetsocket = new Jetsocket({ appId: process.env.JETSOCKET_APP_ID, key: process.env.JETSOCKET_KEY, secret: process.env.JETSOCKET_SECRET, cluster: process.env.JETSOCKET_CLUSTER,});
// Bad: Keys in client codeconst jetsocket = new Jetsocket("APP_KEY", { cluster: "APP_CLUSTER", secret: "APP_SECRET" // Never expose secret on client!});Use environment variables
Section titled “Use environment variables”Store sensitive configuration in environment variables:
# .env fileJETSOCKET_APP_ID=your_app_idJETSOCKET_KEY=your_app_keyJETSOCKET_SECRET=your_app_secretJETSOCKET_CLUSTER=your_clusterENCRYPTION_MASTER_KEY=your_encryption_keyJWT_SECRET=your_jwt_secretValidate webhooks
Section titled “Validate webhooks”Always verify webhook signatures:
app.post("/jetsocket/webhook", (req, res) => { const webhook = jetsocket.webhook(req, { webhookSecret: process.env.WEBHOOK_SECRET });
if (webhook.isValid()) { // Process webhook events webhook.getEvents().forEach(event => { console.log("Webhook event:", event.name); }); res.status(200).send("OK"); } else { res.status(400).send("Invalid webhook"); }});Error Handling
Section titled “Error Handling”Don’t expose sensitive information
Section titled “Don’t expose sensitive information”// Good: Generic error messagesapp.post("/jetsocket/auth", (req, res) => { try { // Authorization logic } catch (error) { console.error("Auth error:", error); res.status(500).send("Internal server error"); }});
// Bad: Exposing internal detailsapp.post("/jetsocket/auth", (req, res) => { try { // Authorization logic } catch (error) { res.status(500).json({ error: "Database connection failed", details: error.message, stack: error.stack }); }});Log security events
Section titled “Log security events”app.post("/jetsocket/auth", (req, res) => { const user = req.user; const channel = req.body.channel_name; const socketId = req.body.socket_id;
console.log(`Auth attempt: User ${user.id} requesting access to ${channel}`);
if (canAccessChannel(user, channel)) { console.log(`Auth success: User ${user.id} granted access to ${channel}`); const authResponse = jetsocket.authorizeChannel(socketId, channel); res.send(authResponse); } else { console.log(`Auth failed: User ${user.id} denied access to ${channel}`); res.status(403).send("Forbidden"); }});Client-Side Security
Section titled “Client-Side Security”Handle connection errors
Section titled “Handle connection errors”jetsocket.connection.bind("error", (error) => { console.error("Connection error:", error);
// Don't show sensitive error details to users showUserMessage("Connection failed. Please try again.");});
const channel = jetsocket.subscribe("private-my-channel");
channel.bind("jetsocket:subscription_error", (error) => { console.error("Subscription error:", error);
if (error.status === 403) { showUserMessage("Access denied"); } else if (error.status === 401) { redirectToLogin(); } else { showUserMessage("Connection failed"); }});Validate client events
Section titled “Validate client events”// Only allow client events on private/presence channelsconst channel = jetsocket.subscribe("private-my-channel");
channel.bind("client-message", (data, metadata) => { // Validate the event data if (!data.message || typeof data.message !== 'string') { console.error("Invalid client event data"); return; }
// Process the message handleMessage(data, metadata.user_id);});Monitoring and Logging
Section titled “Monitoring and Logging”Monitor authorization attempts
Section titled “Monitor authorization attempts”// Track authorization attemptsconst authAttempts = new Map();
app.post("/jetsocket/auth", (req, res) => { const ip = req.ip; const now = Date.now();
// Track attempts per IP if (!authAttempts.has(ip)) { authAttempts.set(ip, []); }
const attempts = authAttempts.get(ip); attempts.push(now);
// Remove old attempts (older than 1 hour) const oneHourAgo = now - 60 * 60 * 1000; const recentAttempts = attempts.filter(time => time > oneHourAgo); authAttempts.set(ip, recentAttempts);
// Check for suspicious activity if (recentAttempts.length > 100) { console.warn(`Suspicious activity from IP: ${ip}`); return res.status(429).send("Too many requests"); }
// Continue with authorization...});Log security events
Section titled “Log security events”function logSecurityEvent(event, details) { const logEntry = { timestamp: new Date().toISOString(), event: event, details: details, ip: req.ip, userAgent: req.get('User-Agent') };
console.log("Security event:", JSON.stringify(logEntry));
// Send to security monitoring service sendToSecurityMonitoring(logEntry);}Compliance and Regulations
Section titled “Compliance and Regulations”GDPR compliance
Section titled “GDPR compliance”// Handle user data deletionapp.delete("/user/:userId", authenticateUser, async (req, res) => { const userId = req.params.userId;
// Verify user can delete this data if (req.user.id !== userId && req.user.role !== 'admin') { return res.status(403).send("Forbidden"); }
// Delete user data await deleteUserData(userId);
// Terminate user connections await jetsocket.terminateUserConnections(userId);
res.json({ success: true });});HIPAA compliance (for healthcare)
Section titled “HIPAA compliance (for healthcare)”// Use encrypted channels for medical dataconst medicalChannel = jetsocket.subscribe("private-encrypted-medical-records");
// Log access to medical datafunction logMedicalDataAccess(userId, recordId) { console.log(`Medical data access: User ${userId} accessed record ${recordId}`); // Send to audit log}Testing Security
Section titled “Testing Security”Test authorization
Section titled “Test authorization”describe("Channel Authorization", () => { test("should deny access to unauthorized users", async () => { const response = await request(app) .post("/jetsocket/auth") .send({ socket_id: "123.456", channel_name: "private-admin-dashboard" }) .set("Authorization", "Bearer user-token");
expect(response.status).toBe(403); });
test("should allow access to authorized users", async () => { const response = await request(app) .post("/jetsocket/auth") .send({ socket_id: "123.456", channel_name: "private-user-123" }) .set("Authorization", "Bearer valid-user-token");
expect(response.status).toBe(200); });});Test input validation
Section titled “Test input validation”describe("Input Validation", () => { test("should reject invalid channel names", async () => { const response = await request(app) .post("/jetsocket/auth") .send({ socket_id: "123.456", channel_name: "invalid-channel-name!" });
expect(response.status).toBe(400); });});Next steps
Section titled “Next steps”- Learn about authentication for user management
- Explore encrypted channels for additional security
- Check out channel authorization for access control
- See examples for secure application patterns