Модель безопасности.
Что защищает записку, что сервер видит и чего не видит, и где границы — сказано прямо, потому что безопасность, которую вы не можете понять, — не безопасность.
Криптография
- Шифрование: каждая записка запечатывается на вашем телефоне с помощью AES-256-GCM (12-байтовый нонс и 16-байтовый тег аутентификации) до какой-либо загрузки. Изменённый шифртекст не проходит аутентификацию, а не расшифровывается в мусор.
- Вывод ключа: ключ получается из HKDF-SHA256 над 32 новыми случайными байтами — ключом фрагмента — плюс случайная 16-байтовая соль. Эти 32 байта попадают в #фрагмент ссылки и больше никуда.
- Парольная фраза (необязательно): если вы её зададите, она растягивается PBKDF2-HMAC-SHA256 за 600 000 итераций и подмешивается в вывод, так что одной ссылки уже недостаточно. Неверные попытки проваливаются локально в браузере получателя и никогда не расходуют просмотры.
- Проверено дважды: реализации на Swift и Kotlin в приложениях与查看器中的 WebCrypto 实现通过独立生成的测试向量相互校验,因此由一方封存的纸条总能在另一方打开。
Ссылка с ключом во фрагменте
Ссылка на записку выглядит как burnpony.app/n/<id>#<key>. Всё после # — это фрагмент URL, а браузеры не отправляют фрагменты в HTTP-запросах — ни на этот сервер, ни на какой-либо другой. Сервер узнаёт случайный ID записки; ключ существует только в самой ссылке и в браузере получателя во время чтения. Вот почему «сервер не может прочитать вашу записку» — это свойство того, как работает веб, а не обещание.
Что сервер хранит — и не может прочитать
Пока записка жива, сервер хранит: случайный ID записки, запечатанный блоб шифртекста, метки времени создания и истечения, лимит и счётчик просмотров, запрашивалось ли уведомление о прочтении, и хеш токена управления, позволяющего вашему приложению досрочно сжечь записку. Если уведомления включены, записывается время открытия. Вот и вся опись. У него никогда нет ключа, текста записки и даже настройки автоскрытия — она едет зашифрованной внутри записки. Ограничение частоты создания использует хеши IP с серверным секретом, которые истекают примерно через два часа, а не сырые журналы адресов.
Сжигание и что остаётся
Когда получен последний разрешённый просмотр, шифртекст удаляется в той же транзакции базы данных, что его отдала. Истёкшие записки удаляются очисткой, запускаемой каждые пять минут. И, что важно, сожжённая записка, истёкшая записка и записка, которой никогда не было, — все возвращают совершенно одинаковый ответ; сервер нельзя использовать как оракул, подтверждающий, что данная ссылка когда-либо была настоящей.
Просмотрщик
Получатели открывают одну самодостаточную страницу: без фреймворков, без cookie, без аналитики, без запросов куда-либо, кроме этого сервера за самой запечатанной запиской. Она несёт строгую Content-Security-Policy (default-src 'none'), директиву noindex и политику no-referrer, чтобы ссылка никогда не утекала через заголовки referrer. Расшифровка происходит во встроенном WebCrypto браузера. Страница достаточно коротка, чтобы проверить её «Просмотром исходного кода» — это цель проектирования, а не случайность.
Уведомления о прочтении, честно
Уведомления включаются по желанию для каждой записки. Получатель видит «Отправитель будет уведомлён, когда эта записка будет открыта» прежде чем раскрыть её. Пуш, который вы получаете, намеренно обобщён — «Записка была открыта.» с приложенным лишь ID записки — так что ничто из содержимого записки не проходит через серверы провайдера пушей.
Модель угроз
BurnPony защищает конфиденциальность и целостность записки от сервера, наблюдателей в сети и всякого, кто позже найдёт URL без его фрагмента — и ограничивает, как долго и сколько раз записку вообще можно прочитать. Он предполагает, что ваш телефон и браузер получателя ведут себя как задокументировано. Он не защищает от вредоносного ПО на любом из концов и от получателя, который копирует, снимает скриншот или фотографирует записку, пока она видна — этого не может ни один инструмент «сжечь после прочтения». Самоуничтожение ограничивает будущий доступ; оно не может контролировать момент чтения.
Честные ограничения
- Ссылка — это ключ. Любой, кто получит полную ссылку (и парольную фразу, если она задана) до того, как записка сгорит, сможет её прочитать. Отправляйте её по каналу, которому доверили бы сам секрет, либо добавьте парольную фразу и передайте её отдельно.
- Экраны можно захватить. 自动隐藏倒计时减少了肩窥的时间;它不是防截图保护。
- Потерян телефон — потеряно управление. Отправленные вами записки продолжают сгорать и истекать по своему расписанию, но без вкладки «Отправленные» в приложении вы теряете возможность сжечь их досрочно или проверить статус.
- Метаданные сетевого уровня существуют. Как и любой веб-сервер, этот при обслуживании запросов видит подключающиеся IP-адреса на сетевом уровне. Веб-сервер перед ретранслятором настроен с отключённым журналированием доступа, а приложение хранит только недолговечные солёные хеши IP, описанные выше.
Точная спецификация
本页的每一项主张都在 странице протокола v1上逐个常量地精确陈述,依据 публичном репозитории中的冻结代码撰写,并可对照共享测试向量核查。
Открыто для проверки
Просмотрщик проверяем сегодня в любой ссылке на записку, а криптоядро, просмотрщик и сервер-ретранслятор планируются к публичному выпуску — см. страницу открытого кода, чтобы узнать, как обстоят дела.
Сообщить об уязвимости
发现了什么?发邮件到 NorseHorse@norsehor.se — 如愿意可用 PGP-ключ加密。报告直达开发者,且因为中间没有委员会,修复上线很快。