Одноразовое значение создаёт и расходует сервер

Перед открытием подключения сервер генерирует непредсказуемую строку `payload`, связывает её с одной попыткой входа и задаёт короткий срок. Клиентская часть лишь передаёт строку в запросе `tonProof`. Нельзя использовать адрес кошелька, метку времени, последовательный номер сессии или общее одноразовое значение: такие данные угадываются или повторяются.

В хранилище нужны состояние попытки, ожидаемый домен, разрешённая сеть и срок. После всех проверок `nonce` переводится из `unused` в `consumed` одной атомарной операцией. Два параллельных запроса с одним доказательством не должны оба увидеть `unused`. При любом несовпадении сессия не создаётся. Для нового входа сервер выпускает другое значение, а не просит кошелёк снова подписать прежнее.

Сессия появляется только после всех проверок

Что именно должен доказать каждый слой
ПроверкаПоложительный результатЕсли пропустить
Ожидаемый домен и его длина в UTF-8Доказательство создано для домена этого приложенияЕго можно принять в неверном контексте
Одноразовое значение сервераДоказательство относится к открытой попытке входаСтарое доказательство можно воспроизвести
walletStateInit → contractAddressStateInit действительно порождает заявленный адресКлиент подменит данные для открытого ключа
Подпись Ed25519Доверенный ключ подписал точный дайджестНет криптографического контроля аккаунта
Разрешённая сетьАккаунт подключён в разрешённой приложением сетиПодпись сама сеть не связывает
Что именно должен доказать каждый слой
Проверка
Ожидаемый домен и его длина в UTF-8
Положительный результат
Доказательство создано для домена этого приложения
Если пропустить
Его можно принять в неверном контексте
Проверка
Одноразовое значение сервера
Положительный результат
Доказательство относится к открытой попытке входа
Если пропустить
Старое доказательство можно воспроизвести
Проверка
walletStateInit → contractAddress
Положительный результат
StateInit действительно порождает заявленный адрес
Если пропустить
Клиент подменит данные для открытого ключа
Проверка
Подпись Ed25519
Положительный результат
Доверенный ключ подписал точный дайджест
Если пропустить
Нет криптографического контроля аккаунта
Проверка
Разрешённая сеть
Положительный результат
Аккаунт подключён в разрешённой приложением сети
Если пропустить
Подпись сама сеть не связывает

Одна перестановка байтов меняет дайджест

Кошелёк подписывает не JSON. Сначала идёт префикс `ton-proof-item-v2/` в UTF-8. Затем записываются `workchain` как знаковое 32-битное число старшим байтом вперёд (`big-endian`) и 256-битный хеш адреса. Следом идут длина домена как беззнаковое 32-битное число младшим байтом вперёд (`little-endian`) и сам домен в UTF-8. После них записываются метка времени как беззнаковое 64-битное число `little-endian` и точные байты `payload` в UTF-8. Сравнивайте `domain.lengthBytes` с фактической длиной UTF-8. Метку времени нормализуйте: SDK представляет её числом, а спецификация передачи — десятичной строкой.

Статическая карта сериализации ton_proof
ФрагментКодированиеПроверка до хеширования
ПрефиксUTF-8 ton-proof-item-v2/Точное значение и порядок
Адресworkchain int32 BE + хеш 256-bit BEПолный исходный адрес из доверенно связанного StateInit
Домен приложениядлина uint32 LE + домен UTF-8Ожидаемый хост и фактическая длина в байтах
Метка времениuint64 LE, секунды UnixДопустимое прошлое окно и будущая погрешность часов
PayloadТочная строка UTF-8Неистёкший неиспользованный nonce этой попытки
Статическая карта сериализации ton_proof
Фрагмент
Префикс
Кодирование
UTF-8 ton-proof-item-v2/
Проверка до хеширования
Точное значение и порядок
Фрагмент
Адрес
Кодирование
workchain int32 BE + хеш 256-bit BE
Проверка до хеширования
Полный исходный адрес из доверенно связанного StateInit
Фрагмент
Домен приложения
Кодирование
длина uint32 LE + домен UTF-8
Проверка до хеширования
Ожидаемый хост и фактическая длина в байтах
Фрагмент
Метка времени
Кодирование
uint64 LE, секунды Unix
Проверка до хеширования
Допустимое прошлое окно и будущая погрешность часов
Фрагмент
Payload
Кодирование
Точная строка UTF-8
Проверка до хеширования
Неистёкший неиспользованный nonce этой попытки

Сервер вычисляет внутренний SHA-256 сообщения, добавляет два байта `0xff` и строку `ton-connect` в UTF-8, затем снова считает SHA-256. Ed25519 проверяется по полученному дайджесту. Не сериализуйте поля в собственном порядке JSON и не используйте пользовательскую строку адреса как готовые байты. Не исправляйте `domain.toLowerCase()` после подписи.

Открытый ключ должен принадлежать заявленному контракту кошелька

Порядок установления аккаунта

  1. 1
    Разобрать walletStateInit

    `BOC` (`Bag of Cells`) — бинарный контейнер сериализованных ячеек TON. Декодируйте из него `walletStateInit`, определите версию кошелька по хешу кода и извлеките открытый ключ из данных по структуре этой версии.

  2. 2
    Пересчитать адрес

    `contractAddress` для того же `workchain` и StateInit обязан совпасть с `account.address`. Иначе доказательство относится к несогласованному набору данных.

  3. 3
    Сверить открытый ключ

    Извлечённый ключ сравните с полем ответа. Для неизвестного контракта кошелька используйте документированный вызов `get_public_key` из цепочки как запасной путь, а не доверяйте клиентскому полю.

  4. 4
    Проверить подпись

    Проверьте Ed25519-подпись точного дайджеста. Ошибка не исправляется сменой формата адреса или повторным хешированием готового дайджеста.

  5. 5
    Проверить правила и израсходовать nonce

    После криптографии сравните домен, метку времени, `payload` и разрешённую сеть. Затем одним условным обновлением отметьте попытку как `consumed` и только после этого выдайте сессию.

Пользовательская, `bounceable` и `raw` формы TON могут обозначать один идентификатор аккаунта, но в байтовую сборку нельзя подставлять строку на глаз. Сначала нормализуйте адрес документированной библиотекой и отдельно сохраните исходную сеть.

Валидная подпись тоже может быть отклонена

Диагностика отказа без повторной подписи
НаблюдениеВероятная границаБезопасное действие
Подпись невернаБайты, ключ или сама подпись не совпалиСохранить поля и сравнить сериализацию по шагам; не выдавать сессию
Подпись верна, nonce неизвестенPayload выпущен другим сервером или потерянОтклонить и начать новую попытку
Подпись верна, nonce использованПовтор того же доказательстваОтклонить без повторного расходования и зафиксировать событие
Домен не совпалДоказательство подписано для другого хоста или иной длины UTF-8Отклонить; не нормализовать несовпадение постфактум
Метка времени вне окнаСтарое доказательство или недопустимое опережение часовЗакрыть попытку и выпустить новый nonce
Сеть не разрешенаAccount.chain не проходит правила приложенияОтклонить отдельно от результата подписи
Диагностика отказа без повторной подписи
Наблюдение
Подпись неверна
Вероятная граница
Байты, ключ или сама подпись не совпали
Безопасное действие
Сохранить поля и сравнить сериализацию по шагам; не выдавать сессию
Наблюдение
Подпись верна, nonce неизвестен
Вероятная граница
Payload выпущен другим сервером или потерян
Безопасное действие
Отклонить и начать новую попытку
Наблюдение
Подпись верна, nonce использован
Вероятная граница
Повтор того же доказательства
Безопасное действие
Отклонить без повторного расходования и зафиксировать событие
Наблюдение
Домен не совпал
Вероятная граница
Доказательство подписано для другого хоста или иной длины UTF-8
Безопасное действие
Отклонить; не нормализовать несовпадение постфактум
Наблюдение
Метка времени вне окна
Вероятная граница
Старое доказательство или недопустимое опережение часов
Безопасное действие
Закрыть попытку и выпустить новый nonce
Наблюдение
Сеть не разрешена
Вероятная граница
Account.chain не проходит правила приложения
Безопасное действие
Отклонить отдельно от результата подписи

`ton_proof` доказывает контроль ключа для точного аккаунта и попытки входа, а не личность человека, право на средства или безопасность приложения. Пользовательский экран подписи всё равно нужно читать как отдельное разрешение. Открытый и закрытый ключи выполняют разные роли, а закрытый никогда не передают серверу.

Это также не транзакция: `ton_proof` ничего не отправляет в блокчейн и не использует `seqno` или `valid_until` сообщения кошелька. Если приложение смешивает вход и перевод, разделите журналы проверки доказательства и следа в блокчейне. Тогда повтор авторизации не создаст новый платёж.

Сервер завершает вход единственной аудиторской записью: идентификатор попытки, хеш `nonce` без исходного секрета, ожидаемый домен, адрес, версию и хеш кода. Запись также хранит источник открытого ключа, проверку времени, разрешённую сеть, результат подписи и единственный переход в `consumed`. Доказательство, StateInit и токен сессии не должны попадать в публичные URL или аналитические события.

Источники

Мы используем прямые ссылки и фиксируем дату проверки. Полный текст чужих материалов не перепечатывается.

  1. TON Docs — How to authenticate users with ton_proof TON Foundation · Проверено 28 августа 2026 г. в 00:04 GMT+5
  2. TON Connect SDK — pinned implementation surface TON Connect · Проверено 28 августа 2026 г. в 00:04 GMT+5
Материал носит информационный характер и не является индивидуальной рекомендацией. Криптоактивы связаны с риском волатильности, технических сбоев, хищения и полной потери средств.