Sessions et Sécurité

Sessions de documents et contrôle d'accès

Un jeton Doconut identifie un document ouvert. Si une requête le transportant était servie, quiconque l'obtiendrait pourrait lire chaque page. Cette page explique ce qu'une session de document contient, combien de temps elle dure, et comment DocImage.axd décide qui peut voir ses pages — sur un serveur unique et dans une ferme web.

Ce que contient une session de document

Chaque OpenDocumentAsync réussi crée une session, conservée en mémoire :

  • le loaded format viewer — l'instance du moteur contenant le document analysé,
  • per-page state — rotation, retournements et les données d'annotation que l'utilisateur applique dans le widget,
  • l'search index optionnel, construit lors de la première recherche (ou chargé depuis un fichier .srh pré-construit),
  • le watermark de session provenant de DocOptions.Watermark.

Durée de vie

Les sessions expirent selon une fenêtre glissante : DocOptions.TimeOut minutes (par défaut 60), réinitialisée à chaque requête présentant le jeton. Lorsqu'une session se termine — par expiration ou par CloseDocument(token) — son moteur de document est libéré et sa mémoire libérée immédiatement.

csharp
// Une session de courte durée pour un aperçu unique
var token = await viewer.OpenDocumentAsync(path, new DocOptions { TimeOut = 10 });

Une requête avec un jeton expiré reçoit une image d'erreur affichant Document session not found. Please re-open document.. La page doit rouvrir le document pour obtenir un nouveau jeton. Sur un serveur unique, le recyclage du pool d'applications met fin à chaque session également.

Les pages ne sont servies qu'à la session qui les a ouvertes

Avec UnsafeMode = false — la valeur par défaut — l'ouverture d'un document accorde l'accès à la session ASP.NET de la requête qui l'a ouverte, en écrivant une entrée secure-{token} dans cette session. DocImage.axd nécessite l'état de session lui‑même, et ne sert une page que lorsque la session demandeuse possède cette entrée. Tout autre navigateur présentant le même jeton reçoit une image d'erreur affichant You Are Not Authorized To View This Page.

csharp
Doconut.DoconutHost.Initialize(options =>
{
    // false (par défaut) : DocImage.axd ne sert une page qu'à la session ASP.NET qui l'a ouverte
    // le document. Le gestionnaire demande l’état de session lui‑même ; il a seulement besoin que le site ne
    // désactive l’état de session dans Web.config.
    options.UnsafeMode = false;
});

Trois conséquences pratiques :

  • Le point d'accès d'ouverture nécessite l'état de session. Les contrôleurs MVC et les pages Web Forms en disposent par défaut. Un IHttpHandler personnalisé ne le fait pas à moins d'implémenter System.Web.SessionState.IRequiresSessionState. Une ouverture qui s'exécute sans session n'a rien à accorder, ainsi le visualiseur affiche « You Are Not Authorized To View This Page. » sur chaque page.
  • L'état de session doit rester activé. <sessionState mode="Off" /> dans Web.config désactive à la fois l'octroi et la vérification, et chaque page est refusée.
  • Le navigateur doit envoyer le cookie de session. Les requêtes du widget sont de même origine, donc elles transportent ASP.NET_SessionId automatiquement. Un client sans jar de cookies — un script, un appel serveur‑à‑serveur, une configuration cross‑origin qui supprime les cookies — est refusé. C'est la fonctionnalité qui fonctionne.

UnsafeMode = true désactive la vérification : toute requête transportant le jeton est servie. C'est un interrupteur global pour chaque document, sans surcharge par document. Laissez‑le false.

Révocation

CloseDocument(token) fait plus que libérer la mémoire : il supprime l'entrée secure-{token} de la session de la requête en cours et révoque le jeton, de sorte qu'un jeton fermé est immédiatement refusé.

Fermes web : tickets signés

Dans une ferme, la requête suivante peut arriver sur un serveur qui n'a jamais vu le document, dans un processus de travail différent, avec sa propre session ASP.NET. L'octroi de session ne peut pas le suivre. Le mode distribué le remplace par un ticket signé lié au navigateur : chaque nœud partage un même magasin pour les sessions de documents et une clé de signature, et DocImage.axd accepte une requête lorsque son ticket est valide pour ce jeton, ce navigateur, et — lorsque l'utilisateur est connecté — cet utilisateur.

La configuration, identique sur chaque nœud :

csharp
// Global.asax.cs - Application_Start
Doconut.DoconutHost.Initialize(
    options => options.UnsafeMode = false,     // la vérification du ticket ne s’exécute que lorsque ceci est false
    services => services.AddDoconutDistributed(d =>
    {
        d.Store             = CloudLocation.FileShare;
        d.FileShareRootPath = @"\\fileserver\doconut-shared";  // un dossier que chaque nœud peut atteindre
        d.SigningKey        = farmKey;                          // au moins 32 octets, le même sur chaque nœud
    }));

Le point d'accès d'ouverture lie alors le ticket au navigateur avant l'ouverture, et renvoie le ticket avec le jeton :

  • définir DocOptions.BrowserId = DoconutFarmBinding.EnsureBrowserId(System.Web.HttpContext.Current, securityOptions) — il émet le cookie doconut-client auquel le ticket est lié — et DocOptions.UserId = DoconutFarmBinding.ResolveUserId(System.Web.HttpContext.Current) ;
  • après OpenDocumentAsync, renvoyer viewer.AccessToken en même temps que le jeton ;
  • dans la page, transmettre les deux au widget : objViewer.View(token, access).

Doconut.MvcSample.WebFarm dans le paquet d'exemples est un nœud de ferme complet et exécutable, et le guide de migration à côté explique chaque étape.

Conservez la clé de signature hors du contrôle de version : elle signe les tickets et chiffre les documents dans le magasin partagé. Un nœud avec une clé différente rejette chaque ticket émis par les autres.

Testez une ferme avec un vrai navigateur, ou un script qui conserve les cookies. Un client sans le cookie doconut-client ne reçoit pas 401 : il reçoit l'image d'erreur « not authorized » avec le statut 200, d'environ 2 KB, alors qu'une vraie page fait plusieurs dizaines à centaines de kilooctets.

Liste de contrôle de production

  • Conservez UnsafeMode = false (la valeur par défaut).
  • Ouvrez les documents à partir de requêtes disposant de l'état de session, et ne désactivez jamais l'état de session.
  • Assurez‑vous que le cookie de session atteint DocImage.axd (même origine, HTTPS, une politique SameSite qui le permet).
  • Dans une ferme, utilisez le mode distribué avec une clé de signature partagée par chaque nœud.
  • Appelez CloseDocument lorsque l'utilisateur quitte un document — la mémoire et la sécurité en bénéficient.
  • Ne jamais enregistrer ou partager les jetons ; traitez‑les comme des identifiants à courte durée de vie.

Cette page était-elle utile ?