This commit is contained in:
2025-08-23 21:37:14 +02:00
parent e20fb6311a
commit 1c5768aab6
2 changed files with 13 additions and 96 deletions

View File

@@ -2,8 +2,6 @@ import eventlet
eventlet.monkey_patch() eventlet.monkey_patch()
from flask import Flask, make_response, render_template, request, jsonify from flask import Flask, make_response, render_template, request, jsonify
from flask_socketio import SocketIO, emit, join_room, leave_room, disconnect from flask_socketio import SocketIO, emit, join_room, leave_room, disconnect
from flask_limiter import Limiter
from flask_limiter.util import get_remote_address
import collections import collections
import threading import threading
import time import time
@@ -23,7 +21,8 @@ logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
app = Flask(__name__) app = Flask(__name__)
socketio = SocketIO(app) app.config['SECRET_KEY'] = os.environ.get('SECRET_KEY', 'dev-secret-key-change-in-production')
socketio = SocketIO(app, cors_allowed_origins="*", async_mode='eventlet')
# Security constants # Security constants
MAX_MESSAGES = 256 MAX_MESSAGES = 256
@@ -33,8 +32,6 @@ MAX_ROOMS_PER_IP = 5
MAX_USERS_PER_ROOM = 50 MAX_USERS_PER_ROOM = 50
ROOM_CLEANUP_INTERVAL = 3600 # 1 hour ROOM_CLEANUP_INTERVAL = 3600 # 1 hour
USER_SESSION_TIMEOUT = 3600 # 1 hour USER_SESSION_TIMEOUT = 3600 # 1 hour
RATE_LIMIT_MESSAGE = "10 per minute"
RATE_LIMIT_ROOM_JOIN = "5 per minute"
# In-memory storage with enhanced security # In-memory storage with enhanced security
chat_rooms = {} chat_rooms = {}
@@ -45,7 +42,6 @@ room_creation_times = {}
ip_room_count = {} # Track rooms created per IP ip_room_count = {} # Track rooms created per IP
failed_password_attempts = {} # Track failed password attempts failed_password_attempts = {} # Track failed password attempts
message_hashes = {} # Store message hashes for duplicate detection message_hashes = {} # Store message hashes for duplicate detection
room_session_keys = {} # Store session keys for each room
class CircularMessageBuffer: class CircularMessageBuffer:
def __init__(self, max_size=MAX_MESSAGES): def __init__(self, max_size=MAX_MESSAGES):
@@ -154,7 +150,6 @@ def cleanup_room(room_id):
room_passwords.pop(room_id, None) room_passwords.pop(room_id, None)
room_creation_times.pop(room_id, None) room_creation_times.pop(room_id, None)
message_hashes.pop(room_id, None) message_hashes.pop(room_id, None)
room_session_keys.pop(room_id, None) # Clean up session keys
def get_client_ip(): def get_client_ip():
"""Get real client IP address""" """Get real client IP address"""
@@ -297,7 +292,6 @@ def handle_join_room(data):
room_keys[room_id] = {} room_keys[room_id] = {}
room_creation_times[room_id] = time.time() room_creation_times[room_id] = time.time()
message_hashes[room_id] = set() message_hashes[room_id] = set()
room_session_keys[room_id] = None # Initialize session key storage
# Store hashed password if provided # Store hashed password if provided
if password: if password:
@@ -499,25 +493,6 @@ def handle_share_session_key(data):
except Exception as e: except Exception as e:
logger.error(f"Error in share_session_key: {str(e)}") logger.error(f"Error in share_session_key: {str(e)}")
@socketio.on('key_exchange')
@require_valid_session
def handle_key_exchange(data):
"""Legacy key exchange handler - redirects to share_session_key"""
try:
# Map old format to new format
room_id = data.get('room_id', '')
target_user = data.get('target_user', '')
encrypted_key = data.get('encrypted_key', '')
if room_id and target_user and encrypted_key:
handle_share_session_key({
'room_id': room_id,
'target_user_id': target_user,
'encrypted_key': encrypted_key
})
except Exception as e:
logger.error(f"Error in legacy key_exchange: {str(e)}")
# Background cleanup task # Background cleanup task
def start_cleanup_task(): def start_cleanup_task():
def cleanup_worker(): def cleanup_worker():
@@ -532,10 +507,6 @@ def start_cleanup_task():
cleanup_thread.start() cleanup_thread.start()
# Error handlers # Error handlers
@app.errorhandler(429)
def ratelimit_handler(e):
return jsonify({'error': 'Rate limit exceeded'}), 429
@app.errorhandler(404) @app.errorhandler(404)
def not_found(e): def not_found(e):
return jsonify({'error': 'Not found'}), 404 return jsonify({'error': 'Not found'}), 404
@@ -547,7 +518,8 @@ def internal_error(e):
if __name__ == "__main__": if __name__ == "__main__":
try: try:
socketio.run(app, debug=True, allow_unsafe_werkzeug=True) start_cleanup_task()
socketio.run(app, debug=True, host='0.0.0.0', port=5000, allow_unsafe_werkzeug=True)
except BrokenPipeError: except BrokenPipeError:
# Suppress noisy broken pipe errors (client disconnects) # Suppress noisy broken pipe errors (client disconnects)
import sys import sys

View File

@@ -6,7 +6,7 @@
<title>Secure Chat Platform</title> <title>Secure Chat Platform</title>
<meta http-equiv="X-Content-Type-Options" content="nosniff"> <meta http-equiv="X-Content-Type-Options" content="nosniff">
<meta http-equiv="X-Frame-Options" content="DENY"> <meta http-equiv="X-Frame-Options" content="DENY">
<script src="https://cdn.jsdelivr.net/npm/socket.io-client@3.1.3/dist/socket.io.js"></script> <script src="https://cdn.jsdelivr.net/npm/socket.io-client@4.7.2/dist/socket.io.min.js"></script>
<style> <style>
/* Enhanced CSS with security considerations */ /* Enhanced CSS with security considerations */
* { * {
@@ -575,9 +575,6 @@
</div> </div>
<script> <script>
// Security-hardened JavaScript with improved key exchange
'use strict';
// Content Security Policy enforcement // Content Security Policy enforcement
if (!window.crypto || !window.crypto.subtle) { if (!window.crypto || !window.crypto.subtle) {
alert('This browser does not support required cryptographic features. Please use a modern browser.'); alert('This browser does not support required cryptographic features. Please use a modern browser.');
@@ -648,7 +645,14 @@
async function initializeApp() { async function initializeApp() {
try { try {
console.log("Initializing secure chat application"); console.log("Initializing secure chat application");
// Initialize socket connection
socket = io({ socket = io({
transports: ['websocket', 'polling'],
reconnection: true,
reconnectionAttempts: MAX_RECONNECT_ATTEMPTS,
reconnectionDelay: 1000,
reconnectionDelayMax: 5000,
timeout: 10000 timeout: 10000
}); });
@@ -678,13 +682,6 @@
const securityIndicator = document.getElementById('securityIndicator'); const securityIndicator = document.getElementById('securityIndicator');
securityIndicator.style.display = 'block'; securityIndicator.style.display = 'block';
setInterval(async function() {
if (currentRoom && sessionKey) {
console.log('Rotating session key...');
await rotateSessionKey();
}
}, 30 * 60 * 1000);
} }
function clearSensitiveData() { function clearSensitiveData() {
@@ -705,7 +702,7 @@
keyPair = await window.crypto.subtle.generateKey( keyPair = await window.crypto.subtle.generateKey(
{ {
name: "RSA-OAEP", name: "RSA-OAEP",
modulusLength: 4096, modulusLength: 2048, // Reduced from 4096 for better performance
publicExponent: new Uint8Array([1, 0, 1]), publicExponent: new Uint8Array([1, 0, 1]),
hash: "SHA-256" hash: "SHA-256"
}, },
@@ -739,38 +736,6 @@
} }
} }
async function rotateSessionKey() {
if (!currentRoom || !roomUsers || Object.keys(roomUsers).length <= 1) {
return;
}
try {
await generateSessionKey();
for (const [userId, publicKeyString] of Object.entries(roomUsers)) {
if (userId !== currentUserId) {
try {
const publicKey = await importPublicKey(publicKeyString);
const encryptedKey = await encryptSessionKey(sessionKey, publicKey);
socket.emit('share_session_key', {
room_id: currentRoom,
target_user_id: userId,
encrypted_key: encryptedKey
});
} catch (error) {
console.error(`Failed to share rotated key with user ${userId}:`, error);
}
}
}
addSystemMessage("🔄 Session key rotated for enhanced security");
} catch (error) {
console.error("Failed to rotate session key:", error);
}
}
async function exportPublicKey(key) { async function exportPublicKey(key) {
try { try {
const exported = await window.crypto.subtle.exportKey("spki", key); const exported = await window.crypto.subtle.exportKey("spki", key);
@@ -1508,26 +1473,6 @@
} }
}, 30000); }, 30000);
// Memory cleanup
setInterval(() => {
if (typeof gc === 'function') {
gc();
}
}, 300000);
let windowHasFocus = true;
window.addEventListener('focus', () => {
windowHasFocus = true;
if (!isConnected && socket) {
socket.connect();
}
});
window.addEventListener('blur', () => {
windowHasFocus = false;
});
// Prevent clickjacking // Prevent clickjacking
if (top !== self) { if (top !== self) {
top.location = self.location; top.location = self.location;