Сразу дисклеймер: я ничего не продаю. share·me, бесплатный open-source проект под AGPL, без тарифов, подписок и регистрации. Это история про то, как обычная задача «передать человеку файл» бесила меня ровно столько раз, что в итоге я сел и написал своё.
Передать файл и не отдать его половине интернета
Сценарий до боли знакомый: надо скинуть коллеге пароль от сервера, или документ, или дамп базы. Открываешь варианты и тихо вздыхаешь.
WeTransfer и аналоги видят всё, что ты загрузил, по определению. Почта и телеграм оставляют файл лежать в плейнтексте навсегда, на всех серверах по пути. PrivateBin отличный, но это только текст, файл на пару гигабайт туда не положишь. А хочется простого: бросил файл, получил ссылку, и сервер при этом физически не может его прочитать. Причём сервер мой.
В какой-то момент я перестал искать и написал share·me.
Идея на салфетке: ключ кладём в ссылку, серверу не показываем
Браузер генерирует случайный ключ и шифрует файл прямо у тебя на клиенте через AES-256-GCM. А ключ отправляется вот сюда:
https://share.example/d/AbC123#k=<base64-ключ>
Всё, что после #, браузер никогда не отправляет на сервер. Этого нет ни в строке запроса, ни в логах сервера, ни в access-логах прокси, который ты воткнул спереди. Ссылка несёт ключ, а сервер видит только шифротекст, включая имена файлов. Вот и весь фокус, до неприличия простой.
Не хочешь ключ в ссылке? Есть парольный режим: ключ выводится через Argon2id (фолбэк PBKDF2). Неверный пароль просто не проходит серверную авторизацию, а она с rate-limit, так что офлайн-перебора нет.
Нюанс, про который туториалы скромно молчат: 5 ГБ в память не положишь
Любой гайд «зашифруй файл через WebCrypto» вызывает encrypt(весь_файл). Попробуй так с загрузкой на 5 ГБ, и вкладка ловит OOM. Реальные файлы надо стримить.
Поэтому крипто-пакет использует сегментированный AES-256-GCM STREAM: файл режется на чанки, каждый шифруется на HKDF-производном подключе со счётчиком-нонсом. Две вещи, на которых я споткнулся и которые стоит назвать вслух:
Нонсы чанков должны быть детерминированными и упорядоченными. Иначе ты не расшифруешь поток, который не буферизовал целиком. Счётчики, а не случайные нонсы.
AES-GCM не key-committing. Один шифротекст можно подделать так, что он чисто расшифруется под двумя разными ключами. Для сервиса обмена файлами это реальная мина, поэтому есть key-committing заголовок, привязывающий шифротекст ровно к одному ключу.
Браузер ↔ API стримят напрямую, Rust-сервис не держит файл в памяти целиком.
Архитектура
Браузер (AES-256-GCM на клиенте) │ ▼ Traefik ──/api/*──► Rust / axum API ──► блобы (диск или S3) + метаданные (SQLite/Postgres) └─────/*──────► Next.js BFF
Один origin через Traefik, а значит, никакого CORS. Тонкий BFF на Next.js (Server Actions) держит owner-токен каждого дропа в httpOnly-cookie; большие блобы идут мимо него, прямо в API. Срок жизни, лимит скачиваний, burn-after-reading и time-lock enforced на сервере, клиент не сможет их обмануть.
Поднять у себя
git clone https://github.com/onokashino/share-me.git && cd share-me docker compose up --build # http://localhost
Указал DOMAIN + ACME_EMAIL, и Traefik сам выпустит сертификат Let’s Encrypt. Готовые образы лежат на ghcr.io. Хватает 1 vCPU / 1 ГБ RAM: упирается в диск и трафик, а не в процессор.
Одна честная оговорка: стороннего аудита пока нет. Криптография специально вынесена в один небольшой пакет с минимумом зависимостей, чтобы её реально можно было прочитать за один присест. Буду рад, если кто-то знающий поищет в ней дыры. Лицензия AGPL-3.0.
gerbert_MX
а зачем вообще ключ?
я себе еще давным давно сделал для хоста систему, что позволяет отдавать файлы или по фиксированному названию или сгенерированная случайная строка. При этом оба варианта можно ограничить как сроком выдачи, так и лимитом на загрузки после которого файл так же пропадает из общего доступа
Это к тому что главная защита это уникальная ссылка, а далее уже файл доступен как обычно любым способом хоть с консоли скачать. Если сервер ваш то проблем компрометации нет. Даже если есть то навернуть внуренее шифрование и все.
MountainGoat
И куча собеседников с телеметрийными расширениями, которые шлют все ссылки каким-то ботам на анализ.
gerbert_MX
Потому и поддержка лимитов на срок или на количество загрузок
в любом случае от ссылки с ключом оно ничем не отличается, кроме оверхеда на шифрование со стороны сервера