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

Lightning Network: полномочия и публичный статус счёта

Оглавление курса «Bitcoin и Lightning: платежи на сайте» · Урок 28 из 30.

К этому моменту у сервера есть инструменты, наблюдения и процедура восстановления. Теперь ограничим, кто может создавать счета, читать персональный статус и управлять узлом. В уроке построим две независимые границы: пользовательскую авторизацию сайта и полномочия backend-адаптера. Результат покажет, почему безопасная кнопка проверки заказа не должна получать административный секрет Lightning-инфраструктуры.

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

Роли сайта и роли узла

Читатель создаёт заказ для доступного курса и смотрит собственный статус. Сервис наблюдения читает известные invoice и фиксирует результат. Отдельный оператор управляет каналами, восстановлением и секретами. Редактор материала не получает полномочий узла только потому, что может менять название курса.

В LND для RPC используется механизм macaroons и ограничения полномочий. Их детали описаны в первичной документации LND. Секрет, позволяющий серверу выполнять действие, не является браузерной cookie читателя. Отдавать его в JavaScript, HTML или публичный endpoint означает переносить разрешение за предусмотренную границу.

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

Право просмотра заказа

На сайте используем серверную сессию. Заказ содержит заранее определённого reader_id, а запрос приносит действующее разрешение. Числовой orderId из URL не является доказательством владения и не должен позволять перебрать чужую историю.

from dataclasses import dataclass

@dataclass(frozen=True)
class Session:
    reader_id: str
    valid_until: int
    revoked: bool


def may_read_order(session, order, now):
    if session is None or session.revoked:
        return False
    if now >= session.valid_until:
        return False
    return session.reader_id == order['reader_id']


def order_response(session, order, observation, now):
    if not may_read_order(session, order, now):
        raise PermissionError('Статус заказа недоступен')
    return {
        'order_id': order['public_id'],
        'course_id': order['course_id'],
        'expected_msat': str(order['amount_msat']),
        'state': observation.state,
        'source_updated_at': observation.observed_at,
    }

Этот пример не реализует создание session и HTTP-cookie. Он показывает место проверки предметного владельца. Настоящий сервер ищет сессию в своём доверенном хранилище; посетитель не может прислать произвольный reader_id и valid_until как готовое подтверждение. Подмена такой записи обошла бы весь смысл модели.

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

Публичный статус не равен полному ответу RPC

Из объекта исключаем внутренний payment request, preimage, секреты клиента, сведения о каналах и административные сообщения. Если читателю требуется действующая строка оплаты, она выдаётся через отдельный контролируемый маршрут с понятной областью. Полный raw-ответ узла не возвращается ради «удобства отладки».

Даже payment hash следует раскрывать только по продуктовой необходимости. Он полезен для связи и поддержки, но общий список hash всех покупателей не нужен публичному каталогу материалов. В журнале приложения безопасные идентификаторы также имеют политику доступа и срок хранения. Отсутствие имён не делает всю платёжную историю автоматически безличной.

Для ошибочного или чужого заказа ответ не должен излишне раскрывать, существует ли он и кто его владелец. Конкретная форма ошибок зависит от API, но область проверки сохраняется. Частые попытки перебора получают ограничение скорости. Это дополняет авторизацию, не заменяя её.

Чтение и изменение разделены

Просмотр статуса использует чтение сохранённого наблюдения или контролируемую сверку. Он не создаёт новый invoice и не отправляет возврат. Подготовка инструмента — изменяющая операция, которая требует отдельного метода, проверки сессии, цены, CSRF-политики и договора идемпотентности.

Нельзя поставить side effect на обычный GET только потому, что браузеру проще открыть ссылку. Промежуточный кеш, предпросмотр или робот способен вызвать такой URL без пользовательского намерения. Тогда сайт начнёт создавать платежные инструменты при просмотре, а сохранённая связь станет ненадёжной. Серверные маршруты имеют явное назначение и защиту.

Webhook поставщика проходит собственный контроль подлинности. Пользовательская cookie не удостоверяет, что сообщение действительно пришло от платёжного источника. И наоборот, подлинный webhook не позволяет выбрать нового получателя заказа из тела запроса. Сопоставление выполняется по ранее сохранённому instrumentId и профилю.

Изоляция секретов и диагностика

Backend-учётные данные не включаем в source control и статическую сборку. Конфигурация содержит ссылки на защищённый источник секретов, а редакционные статьи — только формы и договоры. Транспорт использует проверяемый защищённый канал; нельзя отключить проверку подлинности сервера ради устранения непонятной ошибки подключения.

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

Ожидаемые случаи

Читатель своей действующей сессии получает ограниченный статус. Чужой заказ отклоняется до обращения к узлу. Просроченная сессия не оживает от того, что invoice оплачен. Процесс наблюдения без операторских полномочий не может закрыть канал. Пользовательское «paid» не становится settlement.

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

Проверка роли в глубине маршрута

Защита только начального HTTP-обработчика недостаточна, если внутренний метод доступен через другой маршрут. Доверенная операция получает уже установленный контекст либо сама проверяет необходимые полномочия. Так административная возможность не появляется у нового endpoint случайно.

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

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