На странице
Безопасность
Узнайте, как Paymos защищает балансы токенов с помощью MPC-подписи 2 из 3, HMAC-аутентификации, подписанных вебхуков и аудита.
Paymos — кастодиальный шлюз: мы держим ключи, которыми деньги двигаются в блокчейне. У любого мерчанта тут законный вопрос: что мешает Paymos забрать мои деньги — и что будет, если что-то сломается?
Эта страница отвечает на оба — сначала коротким списком обещаний, потом простым языком с объяснением, почему так. Здесь — гарантии, которые даёт платформа, и что каждая из них вам даёт, а не «чертёж» внутренностей. То, что относится к публичному API (как подписать запрос, как проверить вебхук), описано точно — без этого не подключиться.
Почему ваши деньги в безопасности
Никто — даже Paymos — не уведёт деньги в одиночку
- Пороговое хранение ключей. Ключ, которым подписывается вывод, разрезан на части, и эти части лежат у разных подписантов в разных регионах. Чтобы провести вывод, нужно, чтобы несколько из них подписали вместе. Ни одна машина, ни один сотрудник, ни один дата-центр не держит ключ целиком. Paymos как компания не может в одиночку подписать перевод ваших денег.
- Обязательный белый список. Деньги уходят только на адреса, которые вы одобрили заранее. Даже полный угон ваших API-ключей не позволит придумать новый адрес для вывода.
- По умолчанию только владелец. Право выводить и менять белый список — только у владельца аккаунта, пока вы сами явно его не делегируете, и каждое делегирование пишется в журнал.
- Вывод — по вашему графику. Баланс ваш с момента финальности платежа. Мы не блокируем средства. Выводите хоть раз в час, хоть никогда.
- Раздельные балансы. Баланс каждого мерчанта учитывается отдельно. Мы не складываем средства в общий котёл и не пускаем их в оборот.
Деньги не пропадут незаметно
- Зачисляем только финальные платежи — на такую глубину в блокчейне, что их уже не «откатить».
- Никаких откатов. Сказали «подтверждён» — не отменяем. Если подтверждённый платёж потом «вылетит» из-за реорга, убыток берёт Paymos, а не вы.
- Вывод не уйдёт дважды. У каждого вывода есть ваш ключ идемпотентности.
- Многоуровневые заморозки могут автоматически остановить один актив, если on-chain картина разойдётся с учётом.
- Ликвидность проверяется заранее — вывод либо проходит, либо сразу падает с понятной ошибкой.
- Записи, которые не подделать: каждое значимое действие в аудите, база ежедневно резервируется в зашифрованном виде.
Никаких откатов. Сказали «подтверждён» — не отменяем. Если подтверждённый платёж потом «вылетит» из-за реорга, убыток берёт Paymos, а не вы.
Дальше — каждый пункт простыми словами. Если встречали слова MPC, HMAC, SSRF и хотелось понять, что это — читайте ниже.
Модель угроз
Мы защищаемся от:
- Средства в покое — ключи, которыми можно двигать балансы в блокчейне
- Средства в движении — авторизация вывода и подмена адреса назначения
- Доступ к API — кража ключей, повтор запроса (replay), расширение прав
- Доставка вебхуков — поддельные тела запросов и использование нашей инфраструктуры как SSRF-плацдарма для атаки на вашу
- Операционные риски — случайные разрушительные действия, гонки между серверами, пробелы в аудите
Сознательно вне нашей зоны: безопасность кошелька вашего клиента (ключи у него), риск на стороне получателя после подписи вывода (транзакции необратимы), сторонние сервисы рядом с Paymos и социальная инженерия против вашей команды.
MPC / пороговая подпись — почему деньги нельзя увести в одиночку
MPC (эм-пи-си, Multi-Party Computation — «вычисления несколькими сторонами»). Выше это названо «пороговым хранением» — одно и то же.
В чём проблема. Деньгами в блокчейне управляет приватный ключ — длинная секретная строка. У кого ключ — тот двигает деньги. Обычно это одна строка на одной машине, и отсюда две беды: ключ утечёт — деньги уведут; ключ у одного человека или сервера — он может увести сам, и его одного достаточно взломать.
Что делает MPC. Ключ математически разрезается на части, и части лежат у разных, физически разнесённых подписантов. Чтобы подписать перевод, должно сработать сразу несколько из них — в одиночку ни один не подпишет. Самое неочевидное: полный ключ не собирается нигде и никогда — даже в момент подписи. Каждый подписант делает свой кусок математики со своей частью, куски складываются в готовую подпись, но целого ключа не существует ни в одной точке: ни в памяти, ни на диске, ни на секунду.
Аналогия. Пароль от сейфа — слово «БАНАН». Плохо: отдать его одному охраннику. По-умному: разрезать на зашифрованные обрывки и раздать троим. Когда двое «вместе считают» — не показывая друг другу обрывки — сейф получает правильный ответ и открывается. Но «БАНАН» никто никогда не вводил, не произносил и не записывал. Даже если злоумышленник вечно подсматривает за экраном одного охранника, он видит лишь бессмысленный кусок.
Классический пример такой схемы — «2 из 3»: три подписанта, нужны любые два. Почему так:
- 1 из 1 — одна точка отказа: взломали машину — увели всё.
- 3 из 3 — если одна машина выйдет из строя, деньги заморожены навсегда.
- 2 из 3 — золотая середина: одна машина может выйти из строя (двое оставшихся подпишут), но одной взломанной уже не хватит.
Что это вам даёт — тот самый «не кину»: ни один сотрудник в одиночку не двинет деньги, один взломанный сервер ≠ украденные деньги, и Paymos как компания не уведёт баланс единым решением — нет места с целым ключом и нет «одной кнопки».
Каждый запрос на подпись сам аутентифицирован и привязан к конкретной транзакции; повторённый или изменённый запрос отклоняется ещё до того, как будет задействована хоть одна часть ключа. И у каждого инвойса — свой уникальный адрес для приёма, без переиспользования между клиентами, так что сверка однозначна. Платежи и выводы в песочнице никогда не касаются рабочего контура подписи — тестировать можно без всякого риска для живых денег.
HMAC — как API понимает, что запрос реально от вас
HMAC (Hash-based Message Authentication Code — «код проверки подлинности на основе хеша»). Звучит страшно, суть простая.
В чём проблема. Ваш сервер шлёт нам «создай вывод на 1000 $». У нас два вопроса: это правда вы, а не самозванец? И не подменил ли кто-то запрос по дороге — сумму или адрес? Если просто класть пароль внутрь запроса, любой, кто перехватит трафик, украдёт его и будет слать запросы от вашего имени.
Что делает HMAC. У вас и у нас есть общий секрет (ваш API-секрет). На каждый запрос вы считаете «отпечаток» (подпись) — это хеш от содержимого запроса вместе с секретом. Шлёте запрос и отпечаток, но не сам секрет. Мы своим экземпляром секрета считаем такой же отпечаток. Совпало — значит, это точно вы, и ничего не подменили: измените хоть один символ, и отпечаток не сойдётся.
Аналогия. Сургучная печать на письме. Секрет — это ваш уникальный штамп. Печать все видят, но подделать без штампа не могут. А если письмо вскрыли и переписали — печать перестанет соответствовать содержимому.
Ключевой момент: сам секрет никогда не путешествует по сети. Поэтому, даже записав весь ваш трафик, злоумышленник не вытащит секрет и не сделает новый запрос — у него будут лишь подписи к тем запросам, что вы и так уже отправили. (SHA-256 — это «мельница», перемалывающая любой вход в отпечаток фиксированной длины, не раскручиваемый обратно.)
Точный формат (для интеграции)
Каждый аутентифицированный запрос несёт два заголовка:
Authorization: HMAC-SHA256 {api_key_id}:{base64(signature)}
X-Request-Timestamp: {unix_seconds}
Подпись — это HMAC-SHA256 по канонической строке:
{timestamp}\n{METHOD}\n{path}\n{query}\n{sha256_hex_lower(body)}
Пустое тело подставляется в этот слот как пустая строка — его не хешируют; параметры запроса подписываются ровно в том виде, в каком они в URL. Подробности — на странице Аутентификация.
Защита от повтора (anti-replay)
А переслать ваш старый, записанный запрос — повторить «создай вывод» сто раз? В подпись входит метка времени, и мы отвергаем запрос, если X-Request-Timestamp разошёлся с временем сервера больше чем на ±5 минут (ответ 401). Записанный запрос живёт пять минут и умирает. А вместе с идемпотентностью (см. ниже) даже внутри этих минут повтор не отправит деньги дважды.
Аналогия. Билет с окном входа на пять минут — вчерашний не сработает.
Сравнение «за постоянное время»
Когда мы сверяем две подписи, наивное сравнение обрывается на первом неверном символе — и по времени ответа можно угадать, сколько символов уже верны, и подобрать подпись по байту. Мы сравниваем за постоянное время: ответ «нет» занимает одинаково долго, сколько бы вы ни угадали.
Аналогия. Кодовый замок, который говорит «нет» за одно и то же время — угадали вы одну цифру или пять. Понять, что «теплее», нельзя.
Типы ключей и ротация
Два типа ключей, у каждого жёстко закреплённая зона, проверяется на сервере при каждом вызове:
- Payment-ключ — создавать и читать инвойсы. Не может читать балансы и создавать выводы.
- Payout-ключ — создавать выводы; читать белые списки, счета и балансы. Не может создавать инвойсы.
Секреты меняются без простоя: вы генерируете новый, а предыдущий работает до previous_secret_expires_at (по умолчанию 24 часа). В окне принимаются оба, затем — только новый.
Безопасность вебхуков — тот же HMAC, только наоборот
HMAC выше защищает ваши запросы к нам. Вебхук — это мы стучимся к вам («инвойс оплачен»). Тот же приём наоборот: мы подписываем каждый вебхук секретом вашего эндпоинта, а вы проверяете подпись, прежде чем поверить. Иначе любой, кто узнает ваш webhook-URL, мог бы прислать фейковое «оплачено» и выманить товар.
X-Webhook-Signature: t={unix_seconds},v1={hex_hmac}
Подписывается строка {timestamp}.{request_body}. Схема совпадает со Stripe, поэтому библиотеки проверки от Stripe работают почти без правок; во время ротации секрета мы кладём в заголовок обе подписи. См. Проверка вебхуков.
Защита от SSRF
SSRF (Server-Side Request Forgery — «подделка запроса со стороны сервера»). Вы задаёте webhook-URL, и наши серверы ходят на него. Хитрый злоумышленник укажет URL не наружу, а внутрь нашей сети — на адрес облачных метаданных или внутреннюю панель — и наш сервер сходит туда за него, превратив Paymos в «прокладку» к тому, что видно только из нашей сети.
Мы это предотвращаем: адрес вебхука проверяется на то, что ведёт на публичный адрес — loopback, приватные, внутренние диапазоны и метаданные отклоняются, — а соединение «прибивается» к проверенному адресу, чтобы не было окна для DNS-rebinding. Редиректы не выполняем. Адрес, не прошедший проверку, сразу отбрасывается, поэтому «прощупывать» бесполезно.
Аналогия. Курьер, который доставляет только на реальные внешние адреса, отказывается нести «в серверную этого же здания», проверяет адрес до выхода и не ведётся на записку «вообще-то отнеси вон туда».
Семантика доставки
URL вебхука должен быть на HTTPS (проверяется при сохранении). Доставка — не реже одного раза: дедуплицируйте по идентификатору события на своей стороне (так делают Stripe, GitHub, PayPal). Неуспешные доставки повторяются с экспоненциальной задержкой — всего 11 попыток примерно за 16 часов, полное расписание см. в Доставке и retry, — затем помечаются неуспешными и доступны для переотправки из дашборда. У каждой попытки свой тайм-аут.
Защита вывода — несколько замков подряд
Вывод денег — самое больное место кастодиала, поэтому слоёв тут больше всего:
- Белый список (обязателен). Вывод уходит только на адреса, одобренные заранее. Если для семейства сетей список пуст — вывод нельзя создать вообще; лазейки «первый вывод задаёт адрес» нет. Даже с угнанными ключами слать можно лишь на ваши же кошельки, так что у кражи ключей пропадает смысл. Аналогия: счёт, который переводит только на заранее зарегистрированный список получателей — вор с вашим паролем нового не впишет.
- Только владелец. Создавать выводы и менять белый список может владелец, а не обычная роль «администратора». Не-владельцу право надо выдать явно, и выдача попадёт в аудит.
- Второй фактор (2FA). Шестизначный код из приложения, меняется каждые 30 секунд. Даже укравший пароль не имеет вашего телефона. На вывод из дашборда нужен обязательно.
- Заморозки (стоп-кран). Можем заморозить всё сразу, один токен в одной сети или одного мерчанта. Заморозка по активу включится сама, если on-chain балансы разойдутся с учётом сверх безопасного порога.
- Проверка ликвидности заранее. Вывод сверяется с доступными средствами (с учётом комиссии) до приёма — либо проходит, либо сразу падает с понятной ошибкой, а не «зависает».
- Идемпотентность. Ваш идентификатор заказа — ключ идемпотентности: пришлёте его дважды — получите тот же единственный вывод, а не второй перевод. Аналогия: номерок гардероба; сдадите его дважды — получите одно и то же пальто.
- Безопасность при гонках. Параллельные запросы на вывод для одного аккаунта выстраиваются в очередь на сервере, поэтому два одновременных вызова не проскочат мимо одной проверки баланса.
Авторизация и изоляция
- Каждый запрос проверяется на сервере перед любым действием: среда ключа должна совпадать со средой ресурса, прав ключа должно хватать на операцию, а ресурс должен принадлежать вызывающему. Интерфейс лишь показывает результат этих проверок.
- 404, а не 403. Если ресурса нет — или он есть, но не ваш — вы получите
404. Так не отличить «такой ID существует, но не твой» от «такого ID нет». Аналогия: швейцар говорит «такого нет» и когда человека нет, и когда он есть, но вам нельзя. - Кто вы — берётся из ключа, а не из тела запроса. Подставить чужой идентификатор бесполезно: действовать от имени другого мерчанта, подменив ID, нельзя.
- Гранулярные права. Доступ управляется тонкими ролевыми правами (например: вывод, просмотр баланса, управление вебхуками), каждое выдаётся отдельно. Граница — серверная проверка; дашборд лишь отражает её.
Ограничение частоты запросов
У каждого мерчанта свои лимиты. Превысили — приходит 429 Too Many Requests с заголовком Retry-After, поэтому одна «сбойная» интеграция бьёт только по своей пропускной способности, а не по платформе и не по другим мерчантам.
Финальность в блокчейне — почему «деньги не пропадут»
В блокчейне транзакция «появляется» в блоке быстро, но недавнюю историю цепочка ещё может переписать (реорг) — и то, что выглядело подтверждённым, исчезает. Как сырые чернила: вроде написано, но ещё может смазаться.
Финальность — это дождаться, пока транзакция «закопается» достаточно глубоко, чтобы откатить было нельзя. Чернила высохли. Мы ждём финальность, нужную каждой сети (глубина зависит от суммы: мелочь — быстро, крупное — дольше), и только потом меняем баланс.
Главное обещание: если уже подтверждённый платёж всё же откатит реоргом (большая редкость) — убыток берёт Paymos, а не вы. Как пришёл вебхук о подтверждении — на него можно реагировать сразу. Бонусом: нет чарджбеков (в отличие от карт, которые покупатель может отменить много месяцев спустя) и поздний платёж невозможен в принципе — оплата, подтверждённая после истечения инвойса, на этот истёкший инвойс не зачислится.
Защита данных
В пути: весь трафик по TLS 1.2+, в проде включён HSTS, незашифрованные запросы перенаправляются на HTTPS.
В покое: база ежедневно копируется в зашифрованном виде, резервные копии хранятся со скользящим окном 30 дней; сессионные куки — HttpOnly, Secure, SameSite=Strict; ссылки для входа одноразовые, быстро истекают и хранятся зашифрованными.
Защита от подделки запроса (CSRF): действия в дашборде, меняющие состояние, несут анти-CSRF-токен, который вместе с куками SameSite=Strict гасит межсайтовую подделку.
Журнал аудита: каждое значимое действие фиксирует, кто, что, когда и результат. Чувствительные значения — секреты, пароли, токены, хеши, подписи — затираются ещё до записи.
Операционная устойчивость
- Безопасность на нескольких серверах. Запуск на нескольких серверах никогда не обрабатывает платёж, вывод или вебхук дважды — параллельная работа над одним элементом согласована так, чтобы это произошло ровно один раз. Проверяется автотестами, а не «на честном слове».
- Отчёты об ошибках без утечки данных. Из отчётов о сбоях вычищаются персональные данные и содержимое запросов до того, как они покинут наши системы.
- Самовосстановление инфраструктуры. Автоматические health-проверки выводят нездоровый экземпляр из ротации.
- Безопасные изменения схемы. Миграции базы применяются один раз, по порядку и только «вперёд» — без разрушительных откатов по рабочим данным.
Комплаенс и работа с данными
Для регистрации KYC не требуется. Начать тестирование можно без документов о личности, адресе и бенефициарах; Paymos также не собирает такие документы у ваших клиентов. Paymos не проверяет бизнес, лицензии и категории мерчантов и не проводит санкционный скрининг клиентов — эти обязанности остаются у мерчанта. KYC/KYB может быть запрошен позже при ручной проверке активности, превышении лимитов или по требованию компетентного органа (см. политику AML/KYC).
Данные на стороне клиента. Изначально Paymos не собирает персональные данные ваших клиентов на странице оплаты — ни email, ни телефон, ни имя, ни данные карты. Поток: инвойс → платёжная страница → кошелёк платит → готово. Мы видим адреса кошельков и суммы, а не личности, поэтому ваши риски по GDPR/CCPA через Paymos практически нулевые.
Данные на стороне мерчанта. По вашему аккаунту мы храним название бизнеса, контактный email, страну и метаданные проектов (URL вебхуков, идентификаторы API-ключей). Запрос на удаление по GDPR запускает мягкое удаление на 30 дней, затем — окончательную чистку; в аудите остаётся только хеш удалённой записи.
Резидентность данных. Основные данные — в ЕС. По корпоративным договорам можно закрепить конкретный регион — напишите нам.
Чего мы не делаем
- Не заявляем то, чего не построили. Эта страница описывает систему как она развёрнута. Пункты роадмапа не появляются здесь, пока не выпущены.
- Не держим средства дольше, чем нужно. Деньги на балансе с момента подтверждения; когда выводить — решаете вы.
- Не складываем балансы в общий котёл. Каждый баланс раздельный. Неплатёжеспособность или спор одного мерчанта не дотянется до средств другого. Именно на смешивании общего котла горели биржи вроде FTX.
- Не обещаем безопасность на стороне получателя. После подписи вывода транзакция уже в блокчейне и необратима. Белый список спасает от опечаток и кражи ключей — но не проверит, как поведёт себя одобренный вами адрес.
Сообщить об уязвимости
Присылайте на [email protected].
- Первичный ответ: в течение 24 часов
- Триаж: в течение 72 часов
- Область программы: рабочие
paymos.ioи*.paymos.io, публичный REST API, опубликованные Paymos SDK и модули CMS - Вне скоупа: отказ в обслуживании через лимиты частоты, социальная инженерия, сторонние сервисы (ваш CDN, хостинг и т.п.)
- Bug bounty: публичной программы пока нет; приватное раскрытие значимых находок вознаграждается индивидуально, с упоминанием по запросу.
Пожалуйста, не тестируйте на аккаунтах других мерчантов, не используйте разрушительные техники против прода и не публикуйте находку до того, как мы успеем её починить. Со своей стороны мы подтвердим получение в течение 24 часов, будем держать вас в курсе, упомянем вас (с вашего согласия) при выпуске фикса и не будем преследовать добросовестное исследование в рамках скоупа.