Перейти к основному содержимому
инженериябезопасностьapi
Проектирование некастодиальной торговли: области действия API-ключей и полный отказ от вывода средств

Проектирование некастодиальной торговли: области действия API-ключей и полный отказ от вывода средств

«Некастодиальный» обычно маркетинговое слово. При точном употреблении это архитектурное ограничение, а ограничения такого рода интересны тем, что убирают работу, а не добавляют её.

Торговому боту нужно размещать ордера от вашего имени. Биржи делают это возможным через API-ключи, а API-ключи несут области действия (scopes): чтение, торговля и, на большинстве площадок, вывод средств. Поэтому вопрос проектирования не в том, запрашивать ли разрешения. Вопрос в том, какие именно, и что делать, когда вы получаете больше, чем просили.

Главное

  • Обещание не использовать разрешение слабее, чем его отсутствие. Мы отклоняем ключи с разрешением на вывод средств, а не принимаем и игнорируем их.
  • Отказ даёт более надёжную гарантию, чем доверие, потому что не зависит от того, корректен ли наш код и честна ли наша команда.
  • Если процесс подключения записывает данные в аккаунт пользователя, эти записи должны происходить после решения принять ключ, а не до него. Иначе вы измените чьи-то настройки по пути к отказу.
  • Отказ от хранения средств убирает целый класс сложных задач: нет реестра хранения, нет сверки, нет очереди на вывод.
  • На смену приходит нечто более узкое и требовательное: ваша система должна быть корректна в реальном времени, потому что она никогда ничего не сможет отменить.

1. Отказ от разрешения лучше обещания им не пользоваться

Очевидная реализация состоит в том, чтобы попросить у пользователей ключ с правами на чтение и торговлю и просто никогда не вызывать эндпоинты вывода средств. Так поступает большая часть отрасли, и это не лишено смысла.

Мы делаем строже. Если ключ приходит с разрешением на вывод средств, он отклоняется. Пользователь видит сообщение, точно указывающее, что делать:

У этого API-ключа включено разрешение на вывод средств. Freya никогда не выводит средства, поэтому ключи с таким разрешением не принимаются. Пожалуйста, создайте ключ только с правами на чтение и торговлю.

То же правило действует на всех биржах, которые мы поддерживаем. Ни обхода, ни исключений для администраторов нет.

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

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

  • «Мы никогда не вызываем эндпоинты вывода средств» это утверждение о нашем поведении. Оно верно, пока наш код корректен, наши зависимости чисты, а все, у кого есть доступ к развёртыванию, остаются честными. Ожидать этого разумно, но пользователь не может это проверить.
  • «Ключ не может выводить средства» это утверждение о самом ключе. Оно верно независимо от того, что делает наш код, потому что его обеспечивает биржа, и пользователь может сам подтвердить это в настройках своей биржи.

Второе это более слабый запрос и более сильная гарантия. Предпочитайте конструкции, чьё свойство безопасности обеспечивается кем-то помимо вас, особенно когда у пользователя нет способа проверить вашу сторону.

Есть и меньшая выгода, важная на практике: это делает режим отказа громким. Ключ с избыточными правами перехватывается в момент подключения, на глазах у пользователя, который активно всё настраивает и может исправить это за минуту. Альтернатива в том, что ключ с избыточными правами спокойно работает, и никто ничего не узнаёт, пока это не станет важным.

2. Скажите нет, прежде чем что-либо тронуть

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

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

Теперь подумайте о порядке. Если проверка разрешений происходит после этих записей, то отклонённый ключ всё равно оставляет след. Пользователь передал вам учётные данные, вы изменили настройки в его аккаунте, а затем сказали нет. С его стороны ничто не объясняет, почему конфигурация его биржи отличается от того, какой он её оставил.

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

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

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

3. Что убирает это ограничение

Вот где сделка становится явно выгодной.

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

  • Нет реестра хранения, значит нет сверки внутреннего баланса с внешним
  • Нет очереди на вывод, нет процесса одобрения, нет дневных лимитов на торговый капитал
  • Нет разделения на горячий и холодный кошелёк для этого капитала и нет управления ключами вокруг него
  • Нет вопроса о том, что случится со средствами пользователя, если компания исчезнет

Каждая из них это целая подсистема в кастодиальном продукте, и каждая это место, где могут пропасть деньги. Здесь их нет не потому, что мы их решили, а потому, что мы никогда не взяли на себя то, что их порождает.

Хочу быть точным, а не обобщать: это относится к торговому капиталу. Балансы платформы, используемые для подписок, это отдельный вопрос со своей обработкой. Свойство некастодиальности касается денег, которые торгуют, а это и есть самые важные деньги.

4. Что это не исправляет

Ограничение, убирающее проблемы, также убирает инструменты, которыми вы бы исправляли ошибки, и об этом стоит сказать честно.

Сделки окончательны. При хранении внутренний перевод в принципе можно отменить. Ордер, исполненный на чужом биржевом аккаунте, отменить нельзя. Весь инженерный вес смещается к тому, чтобы быть корректным до отправки запроса, а не сверять после его исполнения. Это дисциплина сложнее, чем кажется, и именно поэтому так много нашей работы посвящено валидации, точности и идемпотентности.

Право на торговлю это всё равно реальное право. Утёкший ключ с правами на чтение и торговлю не может увести средства на внешний адрес, а именно этого люди на самом деле боятся. Но он может размещать ордера. Радиус поражения ограничен, но не равен нулю, и было бы нечестно называть его нулевым.

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

Что я сказал бы другой команде

  • Запрашивайте самую узкую область действия, затем проверяйте, что получили именно её. Просить меньше не то же самое, что получить меньше.
  • Предпочитайте гарантии, обеспечиваемые вне вашего кода, особенно те, что пользователь может проверить, не доверяя вам.
  • Сделайте отказ свободным от побочных эффектов. Если ваш процесс пишет в сторонний аккаунт, всё, что пишет, идёт после решения принять.
  • Кодируйте правило там, куда потребители уже смотрят. Правило, выраженное в общем типе, не требует, чтобы каждый вызывающий о нём помнил.

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


Читайте также

Создайте аккаунт

Примените знания на практике: откройте аккаунт Freya уже сегодня.

Зарегистрироваться
Этот контент предназначен только для образовательных целей и не является финансовой рекомендацией. Торговля криптовалютами связана со значительными рисками. Никогда не инвестируйте больше, чем можете позволить себе потерять.