Перейти к содержанию

Session keys в Web3: ограничения временного ключа

Оглавление курса «Web3: вход, смарт-аккаунты и индексатор» · Урок 26 из 30.

Преподаватель может захотеть опубликовать несколько обновлений курса без полного подтверждения каждой операции основным ключом. Для этого некоторые смарт-аккаунты поддерживают ограниченные ключи сессии. Но такое разрешение нельзя считать обычной cookie-сессией кабинета. В этом уроке опишем узкую политику для курса 17 и покажем, какие поля она обязана связывать, чтобы автоматизация не превратилась в произвольный доступ к реестру.

Мы создаём только учебную модель проверки. Ключи не генерируем, секреты не сохраняем, операции не подписываем. ERC-4337 сам по себе не задаёт универсальный готовый интерфейс session key: поддержку, способ хранения политики и её проверку обеспечивает конкретный аккаунт или модуль. До выбора такого профиля наш объект остаётся договором приложения.

Чем отличается ограниченное разрешение

Серверная сессия читателя позволяет выполнять некоторые HTTP-запросы после входа. Она не предоставляет серверу право публиковать материалы в CourseCatalog. Ключ сессии смарт-аккаунта, напротив, может подписывать сетевые действия в согласованной области. Утечка соответствующего секрета способна привести к реальному выполнению в пределах этой области, поэтому риски и отзыв отличаются.

Для библиотеки зададим минимальную задачу: разрешить publish только курса 17, только в одном экземпляре реестра, с value равным нулю, до определённого срока и в пределах собственной политики количества операций. Разрешение не включает deactivate, перевод средств, изменение владельца аккаунта или произвольный вызов другого контракта. Каждое из этих действий потребовало бы отдельного явного решения владельца.

Срок сам по себе недостаточен. За короткую минуту злоумышленник может отправить множество действий, если область не ограничена. Аналогично лимит количества не ограничивает адрес назначения. Политика должна рассматривать время, предметный охват, бюджет и способ отзыва вместе, не подменяя один компонент другим.

Учебная политика

Вход функции является уже разобранным намерением из нашего приложения. Перед реальным исполнением аккаунт должен проверить действительный calldata и его структуру, а не доверять внешнему полю action. Ложное описание «publish курса 17» не обязано соответствовать переданным байтам.

export function sessionPolicyDecision(intent, policy, context) {
  if (policy.revoked) return { ok: false, reason: 'revoked' };
  if (context.now < policy.validAfter || context.now >= policy.validUntil) {
    return { ok: false, reason: 'time' };
  }
  if (!context.sessionSignatureValid) return { ok: false, reason: 'signature' };
  if (intent.environmentId !== policy.environmentId) {
    return { ok: false, reason: 'environment' };
  }
  if (intent.sender !== policy.account || intent.target !== policy.catalogAddress) {
    return { ok: false, reason: 'address' };
  }
  if (intent.action !== 'publish' || intent.courseId !== 17n || intent.value !== 0n) {
    return { ok: false, reason: 'scope' };
  }
  if (context.executedCount >= policy.maxOperations) {
    return { ok: false, reason: 'count' };
  }
  return { ok: true, policyId: policy.id };
}

Флаг sessionSignatureValid создаёт криптографический адаптер, а число executedCount берётся из доверенного состояния политики. Посетитель не может прислать ноль выполнений и снова получить полный лимит. Идентификатор policyId отделяет новую политику от старой: повторная выдача разрешения не должна случайно оживить отозванную запись с прежним секретом.

В нашем примере ограничение количества относится к принятому приложением определению выполнения. Реальный модуль обязан однозначно определить, когда счётчик расходуется: при допустимой попытке, включении или успешном результате. Иначе неуспешные операции могут стать способом бесконечного расходования газа. Универсального ответа для всех реализаций нет, поэтому его фиксируют рядом с версией модуля.

Байты важнее подписи формы

Нельзя разрешить один метод по четырём байтам selector и не проверить параметры. publish для курса 18 имеет тот же selector, что publish для курса 17. Разрешение должно проверять номер курса и целевой контракт. Кроме того, универсальный account.execute может вызывать другой execute, пакет или делегирование, которое обходит поверхностный фильтр.

Для лаборатории запрещаем составные вызовы и выбираем один определённый маршрут исполнения. Если модуль их поддерживает, каждое вложенное действие должно проверяться согласно его спецификации. Мы не объявляем маленькую JavaScript-функцию защитой Solidity-аккаунта. Она служит перечнем обязательных условий, которые потом реализуются на стороне фактического принятия разрешения.

Дополнительный вопрос — содержимое нового хеша. Даже разрешение только курса 17 допускает замену материала на любой ненулевой хеш, если нет дальнейших ограничений. Если требуется разрешать лишь заранее одобренную редакцию, политика должна связывать конкретный contentHash либо проверяемый список вариантов. Иначе учебная автоматизация получает право менять весь материал курса, а не только отправлять уже выбранную версию.

Отзыв и потеря секрета

Удалить ключ из браузера недостаточно для отзыва. Копия могла уже оказаться у другого участника. Необходимо изменить доверенное состояние аккаунта или модуля так, чтобы прежние разрешения перестали приниматься. Способ, стоимость и момент вступления отзыва в силу относятся к выбранному профилю.

Пока отзыв ещё доставляется, интерфейс показывает «отзыв ожидается» и не обещает немедленную защиту. После подтверждения необходимо выяснить судьбу уже принятых операций: отдельная операция могла быть включена до отзыва. История разрешений сохраняет идентификаторы, сроки и результаты, но не содержит секретных ключей или действительных подписанных пакетов в общедоступной части.

Не предлагаем хранить ключ сессии в localStorage только ради удобства. Скрипт, который получил доступ к странице, способен прочитать доступное ему хранилище. Выбор защищённого устройства, изолированного хранителя или отсутствия автоматических ключей зависит от продукта. Для нашей библиотеки ручная подпись остаётся исходным вариантом до отдельного проекта управления ключами.

Ожидаемый разбор

Действие publish для курса 17 в нужном реестре допускается при действующем сроке, корректной подписи и доступном лимите. Попытка deactivate отклоняется по scope, хотя аккаунт в целом может иметь такое право. Попытка заменить target отклоняется по address. После отзыва тот же подписант больше не допускается независимо от оставшегося количества.

Если операция обещает publish курса 17 в пользовательском описании, а её байты кодируют иной номер, адаптер обязан обнаружить расхождение до разрешения. Если сам адаптер не умеет декодировать выбранный маршрут, безопасный результат — неподдерживаемая операция. Добавлять исключение «пользователь уже вошёл» нельзя: вход в кабинет относится к другой системе доверия.

Полученная политика даёт конкретный результат: можно объяснить пределы автоматизации до выдачи ключа. В следующем уроке свяжем допущенные операции с наблюдаемыми статусами, чтобы ожидание, отказ и успешное исполнение оставались различимыми для пользователя.

Просмотр активных политик

Владелец должен видеть список действующих разрешений с областью, сроком и способом отзыва. Название «редактор курса» само по себе недостаточно: полезно показать номер 17, реестр и разрешённый метод. Так человек может заметить лишнюю политику до использования ключа, а интерфейс не прячет полномочия за общим статусом «подключено».

Оглавление серии