Сессии и безопасность

Сессии документов и контроль доступа

Токен Doconut мощный: кто бы ни представил его, мог бы запросить каждую страницу документа, если бы он не был привязан к открывающей сессии. Эта страница объясняет, что хранит сессия, как долго она живёт и какие проверки UseDoconut() включает по умолчанию.

Что хранит сессия документа

Каждый успешный OpenDocumentAsync создаёт одну сессию в IMemoryCache:

  • загруженный просмотрщик формата (экземпляр движка документа, содержащий разобранный документ),
  • состояние каждой страницы — вращение, отражения и данные аннотаций, которые пользователь применяет в виджете,
  • опциональный search index, создаётся лениво при первом поиске (или загружается из предварительно построенного файла .srh, когда страницы обслуживаются из общего хранилища),
  • водяной знак сессии из DocOptions.Watermark.

Время жизни

Сессии истекают по скользящему окну: DocOptions.TimeOut минут (по умолчанию 60), сбрасывается каждым запросом, представляющим токен. Когда сессия удаляется — из‑за истечения срока или из‑за CloseDocument(token) — её eviction callback освобождает движок документа и сразу освобождает связанную память.

csharp
// Краткосрочная сессия для одноразового предварительного просмотра
var token = await viewer.OpenDocumentAsync(path, new DocOptions { TimeOut = 10 });

Запрос с истёкшим токеном получает изображение ошибки с надписью Document session not found. Please re-open document. — клиент должен заново открыть документ, чтобы получить новый токен.

Встроенная привязка токенов

С UnsafeMode = false (по умолчанию) OpenDocumentAsync привязывает новый токен к ASP.NET session HTTP‑запроса, который его открыл, записывая маркер secure-{token} в эту сессию. Промежуточное ПО Doconut затем отказывает в обслуживании страниц любой другой браузерной сессии:

  • Разный браузер/сессия, представляющие украденный токен → изображение ошибки You Are Not Authorized To View This Page.
  • Промежуточное ПО сессии не зарегистрировано → HTTP 500 Session middleware not configured. Call UseSession() before UseDoconut().

Именно поэтому Quick Start настаивает на AddSession() + app.UseSession() перед веткой Doconut. Два практических следствия:

  • Клиент должен отправлять cookie сессии ASP.NET вместе с запросами страниц. Настройки кросс‑origin, которые удаляют cookies (или API‑клиент без cookie‑jar), не пройдут проверку — это работает как задумано, а не баг.
  • options.UnsafeMode = true полностью отключает привязку. Она существует для контролируемых сценариев; оставьте её false в продакшене на одном узле.

Обратите внимание на написание. В предыдущей библиотеке .NET Standard это свойство называлось UnSafeMode и передавалось в промежуточное ПО; здесь оно называется UnsafeMode и устанавливается один раз внутри AddDoconut().

Привязка токенов контролируется исключительно этим глобальным переключателем UnsafeMode — он включён по умолчанию (UnsafeMode = false) и применяется ко всем сессиям. Нет возможности отключить её для отдельного документа; установка UnsafeMode = true отключает привязку глобально.

Права доступа и аутентифицированные пользователи

Когда UnsafeMode находится в состоянии false, UseDoconut() автоматически вставляет промежуточное ПО доступа к документу перед промежуточным ПО страниц. Не регистрируйте его повторно. Когда запрос содержит токен, он ищет access grant, записанный при открытии документа, и авторизует только если выполнены все следующие условия:

  1. существует грант для токена,
  2. он не истёк (время жизни гранта = TimeOut документа),
  3. ID ASP.NET сессии, сделавшей запрос, совпадает с тем, который открыл документ,
  4. если открывший был аутентифицирован, то требование пользователя NameIdentifier также совпадает.

При ошибках возвращается 403 — в виде PNG‑изображения ошибки для запросов страниц/миниатюр, иначе в виде простого текста. Сообщение и параметр запроса токена берутся из DocumentSecurityOptions (TokenQueryKey, по умолчанию "token"; UnauthorizedMessage, по умолчанию "You Are Not Authorized To View This Page."). Настройте эти параметры через ASP.NET Core DI перед построением приложения. Если состояние сессии недоступно, промежуточное ПО закрывается с ошибкой HTTP 500: ASP.NET Session is required for Doconut document security.

csharp
builder.Services.Configure<Doconut.Security.DocumentSecurityOptions>(options =>
{
    options.TokenQueryKey = "token";
    options.UnauthorizedMessage = "You Are Not Authorized To View This Page.";
});

Основное промежуточное ПО страниц затем проверяет маркер сессии secure-{token} перед обслуживанием документа. При включённом UnsafeMode = true, UseDoconut() пропускает промежуточное ПО доступа, и проверка основного маркера также отключается.

Безопасность на нескольких узлах

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

Ответом не является оставлять документы без защиты. Используйте подписанные билеты доступа через AddDoconutDistributedDocumentSecurity(...), которые заменяют проверку сессии на учётные данные, которые каждый узел может проверять независимо. Полная схема описана в разделе Распределённые развертывания.

Отзыв

CloseDocument(token) не только освобождает память — он также удаляет маркер secure-{token} и отзывает право доступа, так что закрытый токен сразу становится недействительным на обоих уровнях безопасности.

Список проверок для продакшена

  • Оставьте UnsafeMode = false (по умолчанию) на одном узле — этот глобальный переключатель привязывает токены к сессиям.
  • Зарегистрируйте AddDistributedMemoryCache() (MVC и Razor Pages уже имеют его) и AddSession(), и вызовите app.UseSession() перед веткой промежуточного ПО Doconut.
  • Убедитесь, что политика cookie сессии позволяет запросам виджета передавать cookie (SameSite, HTTPS).
  • Используйте CloseDocument, когда пользователь покидает документ — это выгодно и для памяти, и для безопасности.
  • Никогда не логируйте и не делитесь токенами; рассматривайте их как краткоживущие учётные данные.
  • Обслуживание из общего хранилища? Перейдите на подписанные билеты доступа вместо принятия незащищённых документов.

Была ли эта страница полезной?