Zero-Server, Zero-Knowledge, Peer-to-Peer Browser Communicator
Nopeer.pw — это открытый веб-мессенджер нового поколения, работающий полностью на стороне клиента (Client-Side Only). Он предоставляет возможность обмениваться мгновенными сообщениями, передавать файлы любого размера, форматировать текст с помощью Markdown и совершать аудиовызовы без промежуточных серверов хранения данных.
В основе проекта лежит концепция Zero-Knowledge Architecture: сервер не знает, кто с кем общается, не имеет доступа к содержимому переписки и не хранит метаданные или ключи шифрования.
- 1. Архитектура системы
- 2. Подробный разбор функционала
- 3. Безопасность и сквозное шифрование (E2EE)
- 4. Сетевое взаимодействие и протоколы
- 5. Структура проекта
- 6. Развертывание и установка
- 7. Отладка и диагностика
- 8. Лицензия
- Serverless Data Flow: Все данные (текст, медиа, файлы) передаются напрямую из RAM браузера Отправителя в RAM браузера Получателя.
- Ephemeral States: Сообщения и ключи шифрования хранятся исключительно в оперативной памяти (In-Memory). При закрытии вкладки браузера вся история сессии и сессионные ключи уничтожаются без возможности восстановления.
- End-to-End Encryption (E2EE): Никакая незашифрованная информация не покидает пределы браузера пользователя.
┌────────────────────────────────────────────────────────────────────────┐
│ BROWSER A │
│ ┌──────────────┐ ┌─────────────────┐ ┌───────────────────────┐ │
│ │ UI / DOM │ ── │ Engine / Crypto │ ── │ WebRTC (RTCDataChan) │ │
│ └──────────────┘ └─────────────────┘ └───────────────────────┘ │
└───────────────────────────────┬────────────────────────────────────────┘
│
(1) Signaling │ (2) Direct P2P Channel
SDP / ICE Exchange │ Encrypted Data & Media
▼
┌───────────────────────────────┐
│ Node.js Signaling (PeerJS) │
│ * Zero Payload Access │
│ * Temporary Address Router │
└───────────────────────────────┘
▲
│
┌───────────────────────────────┼────────────────────────────────────────┐
│ │ │
│ ┌──────────────┐ ┌────────┴────────┐ ┌───────────────────────┐ │
│ │ UI / DOM │ ── │ Engine / Crypto │ ── │ WebRTC (RTCDataChan) │ │
│ └──────────────┘ └─────────────────┘ └───────────────────────┘ │
│ BROWSER B │
└────────────────────────────────────────────────────────────────────────┘
- Генерация ID: При запуске приложения модуль создаёт случайный идентификатор Peer ID.
- Ссылка-приглашение: Формируется URL вида
https://nopeer.pw/#<peer-id>. Внутренний хэш ссылки (#) не передаётся на веб-сервер при HTTP-запросе, обеспечивая дополнительную приватность. - QR-Код: Автоматическая генерация SVG QR-кода прямо в браузере для быстрого подключения мобильных устройств.
- Рендеринг текста: Встроенный лёгкий парсер преобразует Markdown-разметку (жирный, курсив, заголовки, списки, таблицы) в безопасный HTML.
- XSS Sanitization: Все входящие сообщения фильтруются перед вставкой в DOM для предотвращения атак типа Cross-Site Scripting.
- Подтверждение доставки (ACK): Реализована система квитирования пакетов:
Sent— пакет отправлен в сокет DataChannel.Delivered— противоположный пир получил пакет и отправил обратно служебный кадр ACK.Read— сообщение отрисовано и попало в область видимости окна (Focus/IntersectionObserver).
Передача крупных файлов без переполнения оперативной памяти реализуется через чанкинг:
- Чтение: Файл читается блоками по 16–64 КБ с помощью
FileReader API. - Шифрование: Каждый блок шифруется отдельным вызовом AES-GCM с индивидуальным IV.
- Отправка: Бинарные кадры отправляются в
RTCDataChannel. - Контроль буфера: Используется свойство
bufferedAmount, предотвращающее переполнение сетевого буфера WebRTC. Если буфер переполнен, отправка приостанавливается до событияbufferedamountlow. - Сборка: Получатель собирает зашифрованные и расшифрованные блоб-объекты, формируя финальный
Blobи ссылочный объектURL.createObjectURL().
- Использование
navigator.mediaDevices.getUserMedia({ audio: true }). - Добавление аудио-треков напрямую в
RTCPeerConnection. - Настройка кодека OPUS с адаптивным битрейтом для минимальной задержки при слабом интернет-соединении.
- Возможность отключения микрофона (Mute) на лету без разрыва соединения.
- Кнопка экстренного уничтожения (Purge / Panic Button): Мгновенно обнуляет массивы сообщений в RAM, закрывает WebRTC-соединение, стирает криптографические ключи и перезагружает страницу.
- Auto-Teardown: При закрытии вкладки браузер посылает предупредительный кадр
DISCONNECTпротивоположной стороне, после чего уничтожает все локальные контексты.
Проект опирается на стандартный системный модуль Web Crypto API (crypto.subtle), доступный в современных браузерах.
| Назначение | Алгоритм / Протокол | Параметры |
|---|---|---|
| Асимметричный обмен | ECDH (Elliptic Curve Diffie-Hellman) | Кривая P-256 (secp256r1) |
| Деривация ключей | HKDF (HMAC-based Key Derivation) | Хэш-функция SHA-256 |
| Симметричное шифрование | AES-GCM | Длина ключа: 256 бит |
| Инициализационный вектор | IV / Nonce | 96 бит (12 байт), случайный для каждого кадра |
Шаг 1 — Генерация пары ключей:
Пир А и Пир Б генерируют временные (ephemeral) ECDH ключи в браузере:
KeyPair_A = (K_pubA, K_privA)
KeyPair_B = (K_pubB, K_privB)
Шаг 2 — Обмен публичными ключами:
Публичные ключи K_pubA и K_pubB экспортируются в формат JWK или Raw и передаются друг другу через Сигнальный сервер при открытии сессии.
Шаг 3 — Вычисление Shared Secret (ECDH):
Оба пира независимо вычисляют общий секрет S:
S_A = ECDH(K_privA, K_pubB)
S_B = ECDH(K_privB, K_pubA)
=> S_A = S_B = S
Шаг 4 — Деривация мастер-ключа (HKDF):
Из общего секрета S извлекается 256-битный сессионный ключ K_session:
K_session = HKDF-Expand(HKDF-Extract(salt, S), "nopeer-v1-aes-key", 256)
Диаграмма рукопожатия:
[Client A] [Client B]
│ │
│ ── (1) Gen ECDH KeyPair A ──┐ ┌── (1) Gen ECDH KeyPair B
│ │ │ │
│ ── (2) Send PubKey A ───────┼──[Signal]───┼───────────► │
│ │ │ │
│ ◄───────────────────────────┼──[Signal]───┼─ Send PubKey B (2)
│ │ │ │
│ ── (3) Compute Shared S ────┘ └── (3) Compute Shared S
│ ── (4) Derive AES Key ─────────────────────── Derive AES Key (4) ──
Каждая единица данных (текстовая строка, сервисное сообщение или файловый чанк) перед отправкой превращается в JSON-структуру и зашифровывается.
Алгоритм обработки:
- Создаётся случайный IV размером 12 байт:
crypto.getRandomValues(new Uint8Array(12)). - Объект данных переводится в бинарный вид (
TextEncoder). - Выполняется шифрование:
Ciphertext = AES-GCM-Encrypt(K_session, IV, Plaintext). - В режиме GCM автоматически встроен 128-битный Authentication Tag для контроля целостности.
- Формируется итоговый пакет кадра.
Структура зашифрованного пакета (Binary Payload Format):
+------------------------+----------------------------------------------+
| Field | Size / Format |
+------------------------+----------------------------------------------+
| Initialization Vector | 12 Bytes (Fixed) |
| Encrypted Payload | Variable Size (N Bytes) |
| GCM Authentication Tag | 16 Bytes (Appended to Encrypted Payload) |
+------------------------+----------------------------------------------+
| Угроза / Вектор атаки | Уровень риска | Метод защиты в Nopeer.pw |
|---|---|---|
| Компрометация Signaling сервера | Средний | Сервер пересылает только публичные ключи ECDH. Прослушивание сигнала не позволяет расшифровать трафик. |
| Утечка сообщений с сервера | Высокий | Сообщения не передаются на сервер и не сохраняются на диске. |
| Модификация трафика (MITM) | Высокий | Режим шифрования AES-GCM проверяет подлинность (Auth Tag). Изменённый пакет отбрасывается браузером. |
| Компрометация после сессии | Низкий | Ключи являются временными (ephemeral). После закрытия вкладки ключи удаляются из RAM (Perfect Forward Secrecy на уровне сессии). |
| Анализ размера файлов | Низкий | Файлы передаются фиксированными блоками (чанками), что усложняет сигнатурный анализ. |
Сигнальный сервер написан на Node.js с использованием фреймворка PeerJS Server. Его задача ограничена двумя функциями:
- Выдача уникальных ID для пользователей.
- Трансляция SDP (Session Description Protocol) офферов/ансверов и ICE-кандидатов между клиентами.
Как только P2P-соединение установлено, обмен через сигнальный сервер приостанавливается.
Для преодоления NAT и файрволов используется стандарт ICE (Interactive Connectivity Establishment):
- STUN-серверы: Определяют публичный IP-адрес и порт клиента (используются публичные Google STUN).
- Direct P2P: В ~80% случаев клиенты устанавливают прямое соединение (UDP/TCP).
- TURN-серверы (Relay): Если оба пира находятся за строгим NAT (Symmetric NAT), трафик инкапсулируется и ретранслируется через TURN-сервер в зашифрованном виде. TURN-сервер не может расшифровать AES-GCM трафик.
Для обмена используется RTCDataChannel со следующей конфигурацией:
const dataChannelOptions = {
ordered: true, // Гарантия порядка доставки
maxRetransmits: 3000 // Настройка повторной отправки при потере пакетов
};Типы служебных кадров (Payload Types):
| Тип | Описание |
|---|---|
MSG |
Зашифрованное текстовое сообщение |
FILE_HEADER |
Метаданные файла (имя, размер, тип, количество чанков) |
FILE_CHUNK |
Зашифрованный фрагмент файла |
ACK |
Подтверждение получения кадра |
TYPING |
Уведомление о наборе текста |
PING / PONG |
Проверка активности соединения (Heartbeat) |
nopeer.pw/
├── client/ # Фронтенд-приложение (Client-Side)
│ ├── index.html # Основной HTML-интерфейс
│ ├── css/
│ │ ├── main.css # Главные стили и переменные оформления
│ │ └── components.css # Стили модальных окон, чата и кнопок
│ ├── js/
│ │ ├── app.js # Точка входа, оркестрация модулей и UI
│ │ ├── crypto.js # Web Crypto API: ECDH, AES-GCM, HKDF
│ │ ├── peer.js # Обертка над WebRTC / PeerJS соединением
│ │ ├── file-transfer.js # Модуль чанкинга, контроллер буфера и файлов
│ │ └── ui-render.js # Парсинг Markdown, очистка DOM, уведомления
│ └── assets/ # Иконки, звуковые эффекты уведомлений
│
├── server/ # Сигнальный сервер (Signaling Server)
│ ├── server.js # Точка запуска Node.js / PeerJS сервера
│ ├── config.js # Конфигурация портов и CORS
│ └── package.json # Зависимости сервера
│
├── docs/ # Дополнительная документация и схемы
│ └── SECURITY.md # Детальный аудит безопасности
├── .gitignore
├── LICENSE # Лицензия MIT
└── README.md # Документация проекта
- Node.js: Версия 18.x или выше.
- NPM / Yarn: Актуальная версия менеджера пакетов.
- Браузер: Chrome 90+, Firefox 88+, Safari 14.1+, Edge 90+.
- SSL-сертификат: Обязателен для продакшена. Web Crypto API и WebRTC блокируются браузерами в незашифрованном HTTP контексте (за исключением
localhost).
Клонируйте репозиторий:
git clone https://github.com/TheMk-ctrl/nopeer.pw.git
cd nopeer.pw/serverУстановите зависимости:
npm installЗапустите сигнальный сервер:
# Запуск в режиме разработки
npm run dev
# Продакшен запуск
npm startПо умолчанию сервер начинает listening на порту 9000.
Перейдите в директорию клиента и укажите адрес сигнального сервера в конфигурационном файле client/js/peer.js:
const SIGNALING_CONFIG = {
host: 'your-domain.com', // или 'localhost'
port: 9000,
path: '/myapp',
secure: true // true для HTTPS
};Запустите статический веб-сервер:
cd ../client
npx serve . -p 3000Откройте адрес https://localhost:3000 в вашем браузере.
Для контроля состояния P2P-соединений и криптографических процессов в клиент встроен режим отладки.
Включение Debug-режима
В консоли разработчика браузера (F12) выполните:
localStorage.setItem('debug', 'nopeer:*');
location.reload();Мониторинг WebRTC в браузере
- Chrome / Edge:
chrome://webrtc-internals/— позволяет просматривать графики битрейта, потери пакетов, состояние ICE-кандидатов и RTT. - Firefox:
about:webrtc— детальная диагностика SDP-согласований и DataChannel.
Проект распространяется под лицензией MIT. Вы можете свободно использовать, копировать, модифицировать и разворачивать данный код как в личных, так и в коммерческих целях.
Copyright (c) Nopeer.pw