End-to-End Encrypted WebSocket Chat — без компромиссов по безопасности
Сервер видит только зашифрованный мусор. Приватный ключ никогда не покидает браузер.
Большинство «зашифрованных» мессенджеров шифруют данные на сервере. Это означает, что сервер может прочитать ваши сообщения. SecureChat работает иначе:
- 🔑 Ключи генерируются в браузере через нативный WebCrypto API
- 📡 Сервер — тупой relay: принимает и пересылает зашифрованные байты
- 🚫 Ни одна строчка plaintext никогда не уходит с вашего устройства
- 🗑 При закрытии вкладки сессия уничтожается — ключи не сохраняются
ECDH P-256 → обмен публичными ключами
HKDF-SHA256 → деривация AES-ключа из shared secret
AES-256-GCM → шифрование каждого сообщения
Уникальный IV → для каждого сообщения отдельно
SHA-256 Fingerprint → верификация сессии
Весь крипто — нативный window.crypto.subtle, никаких сторонних библиотек.
| Функция | Статус |
|---|---|
| E2E шифрование (ECDH + AES-256-GCM) | ✅ |
| WebSocket relay-сервер | ✅ |
| Несколько комнат | ✅ |
Автообмен ключами через KEY_ANNOUNCE |
✅ |
| PIN-код на вход | ✅ |
| Светлая / тёмная тема | ✅ |
| Звуковые уведомления (Web Audio API) | ✅ |
| Typing indicator | ✅ |
| Реакции на сообщения | ✅ |
| Ответ на сообщение (reply) | ✅ |
| Исчезающие сообщения (таймер) | ✅ |
| Прикрепление файлов (зашифрованных) | ✅ |
| Read receipts (✓✓) | ✅ |
| QR-код публичного ключа | ✅ |
| Экспорт зашифрованного чата в JSON | ✅ |
| Auto-destroy сессии при закрытии | ✅ |
| История комнаты (последние 50 сообщений) | ✅ |
Мониторинг сервера (/health, /rooms) |
✅ |
git clone https://github.com/YOUR_USERNAME/securechat.git
cd securechatnpm installnpm start
# или указать порт:
node server.js 3001╔══════════════════════════════════════════╗
║ SecureChat WebSocket Server ║
╠══════════════════════════════════════════╣
║ WS: ws://localhost:3001 ║
║ HTTP: http://localhost:3001/health ║
║ Шифрование: E2E (сервер не видит текст) ║
╚══════════════════════════════════════════╝
Открой client.html в браузере — никакого билда не нужно. Это чистый HTML + Vanilla JS.
# Копируем файлы на сервер
scp server.js package.json user@your-server:/opt/securechat/
ssh user@your-server
# На сервере
cd /opt/securechat
npm install
# Открываем порт
sudo ufw allow 3001/tcp
# Запуск через PM2 (рекомендуется)
npm install -g pm2
pm2 start server.js --name securechat
pm2 save && pm2 startupserver {
listen 443 ssl;
server_name chat.yourdomain.com;
location / {
proxy_pass http://localhost:3001;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
}
}После этого в клиенте используй wss://chat.yourdomain.com (WebSocket over TLS).
Алиса Сервер Боб
| | |
|-- генерирует ECDH-пару -----> | |
| | <-- генерирует ECDH-пару ----|
| | |
|-- KEY_ANNOUNCE (pub_A) -----> | ----> KEY_ANNOUNCE (pub_A) -->|
| | |
|<--- KEY_ANNOUNCE (pub_B) ---- | <---- KEY_ANNOUNCE (pub_B) --|
| | |
|-- ECDH(priv_A, pub_B) ------> shared_secret |
| | shared_secret <-- ECDH(priv_B, pub_A)
| | |
|<===== AES-256-GCM encrypted messages =====================>|
| | |
| (сервер видит только байты) |
Сервер никогда не получает приватные ключи. Shared secret вычисляется локально на каждом устройстве.
| Тип | Описание | Payload |
|---|---|---|
JOIN |
Вход в комнату | { roomId, name } |
MSG |
Отправить сообщение | { cipherB64, file?, replyTo?, timerSec } |
TYPING |
Индикатор набора | — |
REACTION |
Реакция на сообщение | { msgId, emoji } |
KEY_ANNOUNCE |
Поделиться публичным ключом | { pubKey } |
PING |
Проверка соединения | — |
| Тип | Описание |
|---|---|
JOINED |
Подтверждение входа + clientId |
HISTORY |
История комнаты (последние 50) |
MSG |
Входящее сообщение |
TYPING |
Кто-то печатает |
REACTION |
Новая реакция |
MEMBER_JOIN |
Участник вошёл |
MEMBER_LEAVE |
Участник вышел |
KEY_ANNOUNCE |
Публичный ключ от участника |
PONG |
Ответ на PING |
GET /health → { status, rooms, clients }
GET /rooms → [ { id, members, messages } ]
securechat/
├── server.js # WebSocket + HTTP сервер (Node.js, только ws)
├── client.html # Клиент (Single-file, zero dependencies)
├── package.json
├── LICENSE
└── README.md
| Минимум | |
|---|---|
| Node.js | 16+ |
| Браузер | Chrome 80+, Firefox 78+, Safari 14+, Edge 80+ |
| Зависимости сервера | ws |
| Зависимости клиента | нет |
WebCrypto API требует HTTPS или localhost. На продакшене используй wss:// + SSL-сертификат (Let's Encrypt).
| Данные | Знает сервер? |
|---|---|
| Текст сообщений | ❌ Никогда |
| Файлы | ❌ Только зашифрованные байты |
| Приватные ключи | ❌ Никогда |
| Публичные ключи | ✅ Да (для relay) |
| Имена участников | ✅ Да |
| IP-адреса | ✅ Да (как любой сервер) |
| Время отправки | ✅ Да |
| Размер сообщения | ✅ Да (metadata leak) |
⚠️ Metadata leak: сервер видит кто, когда и сколько байт отправил. Это неизбежно при WebSocket-архитектуре. Для полной анонимности используй Tor или VPN.
- WebRTC P2P режим (без сервера вообще)
- Signal Protocol (Double Ratchet) для perfect forward secrecy
- PWA + Service Worker (установка как приложение)
- Голосовые сообщения (зашифрованный аудио-поток)
- Multi-device sync через QR
- Self-destruct комнат по таймеру
PR и issues приветствуются. Особенно интересны:
- Улучшения в области безопасности (обязательно опиши threat model)
- WebRTC P2P транспорт
- Тесты для крипто-функций
git checkout -b feature/my-feature
# делаешь изменения
git commit -m "feat: описание"
git push origin feature/my-feature
# открываешь PRMIT — делай что хочешь, но без гарантий. Если используешь в продакшене — проведи security audit.