Sesje i bezpieczeństwo

Sesje dokumentu i kontrola dostępu

Token Doconut jest potężny: każdy, kto go przedstawi, mógłby żądać każdej strony dokumentu, gdyby nie był powiązany z sesją otwierającą. Ta strona wyjaśnia, co przechowuje sesja, jak długo trwa i jakie kontrole UseDoconut() włącza domyślnie.

Co przechowuje sesja dokumentu

Każde udane OpenDocumentAsync tworzy jedną sesję w IMemoryCache:

  • załadowany podgląd formatu (instancja silnika dokumentu przechowująca sparsowany dokument),
  • stan na stronę — rotacja, odbicia i dane adnotacji, które użytkownik stosuje w widżecie,
  • opcjonalny indeks wyszukiwania, tworzony leniwie przy pierwszym wyszukiwaniu (lub ładowany z wstępnie zbudowanego pliku .srh, gdy strony są serwowane z współdzielonego magazynu),
  • znak wodny sesji pochodzący z DocOptions.Watermark.

Okres życia

Sesje wygasają po przesuwanym oknie: DocOptions.TimeOut minut (domyślnie 60), resetowane przy każdym żądaniu, które przedstawia token. Gdy sesja zostaje usunięta — z powodu wygaśnięcia lub CloseDocument(token) — jej funkcja wywołania przy usunięciu zwalnia silnik dokumentu i natychmiast zwalnia powiązaną pamięć.

csharp
// Krótkotrwała sesja do jednorazowego podglądu
var token = await viewer.OpenDocumentAsync(path, new DocOptions { TimeOut = 10 });

Żądanie z wygasłym tokenem otrzymuje obraz błędu z napisem Document session not found. Please re-open document. — klient musi ponownie otworzyć, aby uzyskać nowy token.

Wbudowane powiązanie tokena

Przy użyciu UnsafeMode = false (domyślnie), OpenDocumentAsync wiąże nowy token z sesją ASP.NET żądania HTTP, które go otworzyło, zapisując znacznik secure-{token} w tej sesji. Middleware Doconut odmawia wtedy serwowania stron innym sesjom przeglądarki:

  • Inna przeglądarka/sesja przedstawiająca skradziony token → obraz błędu You Are Not Authorized To View This Page.
  • Middleware sesji nie zarejestrowany → HTTP 500 Session middleware not configured. Call UseSession() before UseDoconut().

Dlatego Quick Start wymaga AddSession() + app.UseSession() przed gałęzią Doconut. Dwie praktyczne konsekwencje:

  • Klient musi wysyłać ciasteczko sesji ASP.NET wraz z żądaniami stron. Konfiguracje cross-origin, które usuwają ciasteczka (lub klient API bez pojemnika na ciasteczka), nie przejdą kontroli — to działająca funkcja, a nie błąd.
  • options.UnsafeMode = true wyłącza powiązanie całkowicie. Istnieje dla kontrolowanych scenariuszy; pozostaw je false w produkcji na pojedynczym węźle.

Zwróć uwagę na pisownię. Poprzednia biblioteka .NET Standard nazywała tę właściwość UnSafeMode i była przekazywana do middleware; tutaj jest UnsafeMode i jest ustawiana raz wewnątrz AddDoconut().

Powiązanie tokena jest kontrolowane wyłącznie przez ten globalny przełącznik UnsafeMode — jest włączony domyślnie (UnsafeMode = false) i dotyczy każdej sesji. Nie ma możliwości wyłączenia per-dokument; ustawienie UnsafeMode = true wyłącza powiązanie globalnie.

Przydziały dostępu i uwierzytelnieni użytkownicy

Gdy UnsafeMode jest false, UseDoconut() wstawia middleware dostępu do dokumentu automatycznie przed middleware stron. Nie rejestruj go ponownie. Gdy żądanie niesie token, wyszukuje przydział dostępu zapisany przy otwieraniu dokumentu i autoryzuje tylko wtedy, gdy wszystkie te warunki są spełnione:

  1. przydział istnieje dla tokena,
  2. nie wygasł (czas życia przydziału = TimeOut dokumentu),
  3. identyfikator sesji ASP.NET żądającego pasuje do tego, który otworzył dokument,
  4. jeśli otwierający był uwierzytelniony, roszczenie NameIdentifier żądającego użytkownika również pasuje.

Niepowodzenia zwracają 403 — jako obraz PNG błędu dla żądań stron/miniatur, w przeciwnym razie jako zwykły tekst. Wiadomość i klucz zapytania tokena pochodzą z DocumentSecurityOptions (TokenQueryKey, domyślnie "token"; UnauthorizedMessage, domyślnie "You Are Not Authorized To View This Page."). Skonfiguruj te opcje przez ASP.NET Core DI przed budowaniem aplikacji. Jeśli stan sesji jest niedostępny, middleware zamyka się z 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.";
});

Podstawowy middleware stron weryfikuje znacznik sesji secure-{token} przed serwowaniem dokumentu. Przy użyciu UnsafeMode = true, UseDoconut() pomija middleware dostępu, a sprawdzenie znacznika podstawowego jest również wyłączone.

Bezpieczeństwo w kilku węzłach

Tokeny powiązane z sesją zakładają, że węzeł serwujący stronę również widział żądanie, które otworzyło dokument. To założenie nie obowiązuje, gdy kilka węzłów serwuje te same dokumenty z współdzielonego magazynu: żądanie strony może trafić na węzeł, który nigdy nie widział otwarcia, a kontrola per-sesja odrzuca je.

Rozwiązaniem nie jest pozostawianie dokumentów niechronionych. Użyj podpisanych biletów dostępu przez AddDoconutDistributedDocumentSecurity(...), które zastępują kontrolę sesji poświadczeniem, które każdy węzeł może zweryfikować niezależnie. Pełna konfiguracja znajduje się w Rozmieszczonych wdrożeniach.

Unieważnianie

CloseDocument(token) nie tylko zwalnia pamięć — usuwa także znacznik secure-{token} i unieważnia przydział dostępu, więc zamknięty token jest natychmiast nieaktywny na obu warstwach bezpieczeństwa.

Lista kontrolna dla produkcji

  • Utrzymaj UnsafeMode = false (domyślnie) na pojedynczym węźle — ten globalny przełącznik wiąże tokeny z sesjami.
  • Zarejestruj AddDistributedMemoryCache() (aplikacje MVC i Razor Pages już go mają) oraz AddSession() i wywołaj app.UseSession() przed gałęzią middleware Doconut.
  • Upewnij się, że polityka ciasteczek sesji pozwala żądaniom widżetu przenosić ciasteczko (SameSite, HTTPS).
  • Użyj CloseDocument, gdy użytkownik opuszcza dokument — zarówno pamięć, jak i bezpieczeństwo zyskują.
  • Nigdy nie loguj ani nie udostępniaj tokenów; traktuj je jako krótkotrwałe poświadczenia.
  • Serwujesz z współdzielonego magazynu? Przejdź na podpisane bilety dostępu zamiast akceptować niechronione dokumenty.

Czy ta strona była pomocna?