Sesiones y Seguridad

Sesiones de documentos y control de acceso

Un token Doconut identifica un documento abierto. Si cualquier solicitud que lo lleva fuera atendida, quien lo obtenga podría leer cada página. Esta página explica qué contiene una sesión de documento, cuánto tiempo dura y cómo DocImage.axd decide quién puede ver sus páginas — en un servidor y en una granja web.

Qué contiene una sesión de documento

Cada OpenDocumentAsync exitosa crea una sesión, mantenida en memoria:

  • el visor de formato cargado — la instancia del motor que contiene el documento analizado,
  • estado por página — rotación, volteos y los datos de anotación que el usuario aplica en el widget,
  • el índice de búsqueda opcional, creado en la primera búsqueda (o cargado desde un archivo preconstruido .srh),
  • la marca de agua de sesión de DocOptions.Watermark.

Tiempo de vida

Las sesiones expiran en una ventana deslizante: DocOptions.TimeOut minutos (por defecto 60), reiniciada por cada solicitud que presente el token. Cuando una sesión termina — por expiración o por CloseDocument(token) — su motor de documento se libera y su memoria se libera inmediatamente.

csharp
// Una sesión de corta duración para una vista previa puntual
var token = await viewer.OpenDocumentAsync(path, new DocOptions { TimeOut = 10 });

Una solicitud con un token expirado recibe una imagen de error que muestra Document session not found. Please re-open document.. La página debe abrir el documento nuevamente para obtener un nuevo token. En un solo servidor, reciclar el pool de aplicaciones también finaliza todas las sesiones.

Las páginas se sirven solo a la sesión que las abrió

Con UnsafeMode = false — el valor predeterminado — abrir un documento otorga acceso a la sesión ASP.NET de la solicitud que lo abrió, escribiendo una entrada secure-{token} en esa sesión. DocImage.axd requiere el propio estado de sesión, y sirve una página solo cuando la sesión solicitante contiene esa entrada. Cualquier otro navegador que presente el mismo token recibe una imagen de error que muestra You Are Not Authorized To View This Page.

csharp
Doconut.DoconutHost.Initialize(options =>
{
    // false (el predeterminado): DocImage.axd sirve una página solo a la sesión ASP.NET que la abrió
    // el documento. El controlador solicita el estado de sesión por sí mismo; solo necesita que el sitio no
    // desactive el estado de sesión en Web.config.
    options.UnsafeMode = false;
});

Tres consecuencias prácticas:

  • El punto final de apertura necesita estado de sesión. Los controladores MVC y las páginas Web Forms lo tienen por defecto. Un IHttpHandler personalizado no lo tiene a menos que implemente System.Web.SessionState.IRequiresSessionState. Una apertura que se ejecuta sin sesión no tiene nada que conceder, por lo que el visor muestra "No está autorizado a ver esta página." en cada página.
  • El estado de sesión debe permanecer activo. <sessionState mode="Off" /> en Web.config desactiva tanto la concesión como la verificación, y cada página es rechazada.
  • El navegador debe enviar la cookie de sesión. Las solicitudes del widget son de mismo origen, por lo que transportan ASP.NET_SessionId automáticamente. Un cliente sin un almacén de cookies — un script, una llamada servidor a servidor, una configuración de origen cruzado que elimina cookies — es rechazado. Esa es la característica en funcionamiento.

UnsafeMode = true desactiva la verificación: cualquier solicitud que lleve el token es atendida. Es un interruptor global para cada documento, sin anulación por documento. Déjelo false.

Revocación

CloseDocument(token) hace más que liberar memoria: elimina la entrada secure-{token} de la sesión de la solicitud actual y revoca el token, de modo que un token cerrado es rechazado de inmediato.

Granjas web: tickets firmados

En una granja, la siguiente solicitud puede llegar a un servidor que nunca vio el documento, en un proceso de trabajo diferente, con su propia sesión ASP.NET. La concesión de sesión no puede seguirla. El modo distribuido lo reemplaza con un ticket firmado vinculado al navegador: cada nodo comparte un almacén para sesiones de documentos y una clave de firma, y DocImage.axd acepta una solicitud cuando su ticket es válido para ese token, ese navegador y — cuando el usuario ha iniciado sesión — ese usuario.

La configuración, idéntica en cada nodo:

csharp
// Global.asax.cs - Application_Start
Doconut.DoconutHost.Initialize(
    options => options.UnsafeMode = false,     // la verificación del ticket solo se ejecuta cuando esto es false
    services => services.AddDoconutDistributed(d =>
    {
        d.Store             = CloudLocation.FileShare;
        d.FileShareRootPath = @"\\fileserver\doconut-shared";  // una carpeta a la que cada nodo puede acceder
        d.SigningKey        = farmKey;                          // al menos 32 bytes, el MISMO en cada nodo
    }));

El punto final de apertura entonces vincula el ticket al navegador antes de abrir, y devuelve el ticket junto con el token:

  • establecer DocOptions.BrowserId = DoconutFarmBinding.EnsureBrowserId(System.Web.HttpContext.Current, securityOptions) — emite la cookie doconut-client a la que está ligado el ticket — y DocOptions.UserId = DoconutFarmBinding.ResolveUserId(System.Web.HttpContext.Current);
  • después de OpenDocumentAsync, devolver viewer.AccessToken junto al token;
  • en la página, pasar ambos al widget: objViewer.View(token, access).

Doconut.MvcSample.WebFarm en el paquete de ejemplos es un nodo de granja completo y ejecutable, y la guía de migración junto a él explica cada paso.

Mantenga la clave de firma fuera del control de versiones: firma los tickets y cifra los documentos en el almacén compartido. Un nodo con una clave diferente rechaza cada ticket emitido por los demás.

Pruebe una granja con un navegador real, o un script que conserve cookies. Un cliente sin la cookie doconut-client no recibe 401: recibe la imagen de error "not authorized" con estado 200, alrededor de 2 KB, donde una página real tiene decenas a cientos de kilobytes.

Lista de verificación para producción

  • Mantenga UnsafeMode = false (el valor predeterminado).
  • Abra documentos desde solicitudes que tengan estado de sesión, y nunca desactive el estado de sesión.
  • Asegúrese de que la cookie de sesión llegue a DocImage.axd (mismo origen, HTTPS, una política SameSite que lo permita).
  • En una granja, use el modo distribuido con una clave de firma compartida por cada nodo.
  • Llame a CloseDocument cuando el usuario abandone un documento — la memoria y la seguridad se benefician.
  • Nunca registre ni comparta tokens; trátelos como credenciales de corta duración.

¿Fue útil esta página?