No estoy seguro de que el tiempo este haya pertenecido en Seguridad o en Revisión de Código, publicando aquí por ahora.
Para mejorar el rendimiento percibido en nuestra plataforma SaaS basada en la web, estamos considerando almacenar en caché ciertos resultados de llamadas API en IndexedDB para que sirvan como resultados 'interinos' mientras esperamos los resultados de la red la próxima vez que llamemos al punto final con esos parámetros . Sin embargo, los datos devueltos son potencialmente confidenciales, por lo que queremos protegerlos mientras se encuentran en el disco duro del usuario.
Estamos tomando las medidas de seguridad web habituales: enviamos todos los encabezados HTTP "nunca se caché", así como HSTS y los encabezados de CSP apropiados ('estrictamente dinámico' en script-src, 'ninguno' en objeto-src). Verificamos CSRF y verificamos que el usuario esté autorizado para acceder a cualquier punto final dado. En resumen, estamos bastante seguros de que podemos mantener la información segura y protegida hasta que llegue al navegador. (Si he olvidado algo importante, por favor dígame).
Nuestra implementación de encriptación propuesta es así: en la solicitud de página, tenemos SHA-256 información de hash exclusiva de ese usuario para crear una clave de 256 bits. La clave está incrustada en el HTML de la página justo encima del script que lo lee en un cierre (no en una propiedad) y lo elimina del marcado.
Cuando solicitamos datos de la API, realizamos una llamada AJAX de manera normal, pero también tomamos la cadena JSON, usamos TextEncoder para convertirlo en una matriz de bytes, luego usamos SubtleCrypto para cifrarlo con AES-GCM antes de finalmente almacenándolo en una tabla IndexedDB.
(El nombre de la tabla es un hash SHA-256 del nombre del punto final de la API, y el ID de la fila es un hash similar de los parámetros. El IV para el cifrado son los primeros 12 bytes del nombre de la tabla.)
La próxima vez que solicitemos ese punto final con esos parámetros, recuperamos la fila, la desciframos, TextDecode y JSON.parsela, pasándola en una devolución de llamada (a menos que la llamada de red simultánea haya finalizado primero).
De esa manera, los datos se cifran tanto en tránsito como en reposo.
Por lo que podemos ver, tenemos dos amenazas de las que preocuparnos:
Personas con acceso físico a la máquina
Por lo que entiendo, el AES-GCM forzado con una clave de 256 bits es prácticamente imposible. Podrían instalar malware para intentar y arrebatar la clave de cifrado la próxima vez que la víctima inicie sesión (o simplemente ver la fuente), pero en ese momento hemos perdido sin importar porque entonces solo podían ver directamente la información que buscaban.
Alguien que descubre una vulnerabilidad XSS en nuestro sitio
Nuevamente, en este punto estamos atornillados, pero con un CSP estricto y la clave almacenada en un cierre, debería ser extremadamente difícil robar la clave de cifrado.
¿Hay otras consideraciones en las que no hayamos pensado, y si es así, qué se puede hacer al respecto?