Parece que su objeción a permitir que la sesión permanezca abierta mientras el navegador esté abierto es el problema de los ataques automáticos. Desafortunadamente, actualizar el token en cada carga de página solo disuade a los atacantes más aficionados.
En primer lugar, supongo que estamos hablando de ataques dirigidos específicamente a su sitio. (Si estamos hablando de los bots que simplemente deambulan y envían varios formularios, esto no solo no los detendría, sino que hay formas mucho mejores y más fáciles de hacerlo). Si ese es el caso, y estoy apuntando a mi sitio, esto es lo que haría mi bot:
- Cargar página de formulario.
- Leer token en la página del formulario.
- Envíe una solicitud automática con ese token.
- Vaya al paso 1.
(O, si investigué su sistema lo suficiente, me daría cuenta de que si incluyera el encabezado "esto es AJAX" en cada solicitud, podría quedarme con un token para siempre. O me daría cuenta de que el token es mi ID de sesión, y enviar mi propio PHPSESSID
galleta.)
Este método de cambiar el token en cada carga de página no haría absolutamente nada para detener a alguien que realmente quería para atacarte tan mal. Por lo tanto, dado que el token no tiene ningún efecto sobre la automatización, concéntrese en sus efectos sobre CSRF.
Desde la perspectiva de bloquear CSRF, crear un token y mantenerlo hasta que el usuario cierre el navegador parece lograr todos los objetivos. Se derrotan los ataques CSRF simples y el usuario puede abrir varias pestañas.
TL;DR:Actualizar el token una vez en cada solicitud no aumenta la seguridad. Opte por la usabilidad y haga un token por sesión.
¡Sin embargo! Si está extremadamente preocupado por los envíos de formularios duplicados, accidentales o de otro tipo, este problema aún se puede resolver fácilmente. La respuesta es simple:use dos fichas para dos trabajos diferentes.
El primer token permanecerá igual hasta que finalice la sesión del navegador. Este token existe para evitar ataques CSRF. Se aceptará cualquier envío de este usuario con este token.
El segundo token se generará de forma única para cada formulario cargado y se almacenará en una lista en los datos de sesión del usuario de tokens de formulario abierto. Este token es único y se invalida una vez que se usa. Los envíos de este usuario con este token se aceptarán una vez y solo una vez.
De esta manera, si abro una pestaña en el Formulario A y una pestaña en el Formulario B, cada una tiene mi token personal anti-CSRF (se ocupa de CSRF) y mi token de formulario único (se ocupa de volver a enviar el formulario). Ambos problemas se resuelven sin ningún efecto adverso en la experiencia del usuario.
Por supuesto, puede decidir que eso es demasiado para implementar para una característica tan simple. Creo que lo es, de todos modos. Independientemente, existe una solución sólida si la desea.