Самый быстрый способ отдать пароль базы — ссылка. Беда в том, что по пути она проходит браузер, прокси, источник и пачку логов. Контролировать нужно не «звучит ли шифр безопасно», а на каком слое появляется ключ.
Ключ в ?key= — часть HTTP-запроса и почти наверняка попадёт в access log. Ключ после # браузер отрезает до запроса документа. Это не обещание продукта, а правило fragment в URI.
Что оставляет ключ в query
Query (от ? до #) входит в цель запроса. При /s.html?id=abc&key=MYSECRET уходят путь и query. Источник, Nginx, CDN, WAF и APM это видят. Access log обычно пишет URI, и key=MYSECRET оказывается в одной строке со статусом.
Эту строку трудно вычистить: ротация, бэкапы, SIEM, «скопируй этот 500». Проверяется протокол, не внутренний регламент ЦОД. Fragment закрывает серверную копию, не все копии на устройстве.
HTTPS не мешает источнику читать query
TLS закрывает путь. После терминации источник видит строку запроса открытым текстом. Query виден; fragment в этом запросе даже не присутствует.
Куда в HTTP уходит то, что после #
Спецификация требует снять fragment до запроса документа. В HTTP-строке этой колонки нет; прокси и источник не запишут её в access log. После загрузки скрипт читает location.hash — это память клиента. Сервер всё время видит только шифротекст.
Одноразовая ссылка MyPassGen: AES-256-GCM в браузере, 256-битный ключ в Base64URL после #; на сервер уходят шифротекст и метаданные. Получатель не регистрируется. После первого чтения шифротекст сжигается.
| Кусок URL | Пример | Видит ли сервер / прокси | Место для ключа? |
|---|---|---|---|
| Путь | /s.html | Да, в access log | Только id шифротекста |
| Query | ?id=abc&key=… | Весь, по умолчанию в лог | Нет; key= — запись в лог |
| Fragment | Ключ после # | В HTTP его нет | Да, канал «только браузер» |
| Тело | JSON при создании | Источник видит | Только шифротекст и несекретные метаданные |
«Нулевое знание» здесь узко: сервер хранит и не может открыть. Полная ссылка — это уже ключ; без куска после # не открыть.
Проверить в Сети, ушёл ли ключ
Подготовьте ссылку с решёткой. Скопируйте то, что после #.
Сеть и Preserve log. Полный URL в адресной строке — это UI браузера, не ушедшее в сеть.
Откройте ссылку. Request URL должна обрываться до #.
Ищите кусок ключа по всей панели.
Сравните ошибку: тот же ключ в ?key= (тест) должен попасть в URL.
При создании ищите исходник в POST. Должен быть шифр, не текст. Получатель не входит.
Что это доказывает
Сеть отвечает, попали ли ключ или исходник в HTTP в этом действии. Расширения и XSS она не видит.
Fragment закрывает лог, не пересылку ссылки
Кто получил путь плюс fragment, откроет шифротекст до сжигания. Скриншоты и пересылки HTTP не запрещает. Современный Referer обычно без fragment, но location.href в аналитике это не спасёт. История браузера хранит полный URL. XSS читает location.hash.
Что должно остаться после #
На устройстве и только через fragment получателю — то, что восстанавливает открытый текст: симметричный ключ. На сервер могут уйти бесполезные ему данные: шифротекст, id, срок и число чтений. Лимит ссылки — 32 КБ. Большой файл сначала шифруйте локально в .lock / .enc. После первого чтения сервер удаляет шифротекст. Копии открытого текста нет.
Когда нужно отдать ключ, не записав его в лог
Почта и мессенджеры оставляют пароль в истории. Диск по умолчанию читаем сервером. Ключ в query просит прокси записать его. Для короткого секрета нужен локальный шифр, ключ вне HTTP и исчезновение после чтения. Так устроена одноразовая ссылка MyPassGen. Больше 32 КБ — файловый ящик. О том, почему расчёт не должен уходить на сервер, — почему расчёт должен оставаться в браузере.