Forma correcta de eliminar las cookies del lado del servidor

141

Para mi proceso de autenticación, creo un token único cuando un usuario inicia sesión y lo coloca en una cookie que se utiliza para la autenticación.

Entonces enviaría algo como esto desde el servidor:

Set-Cookie: token=$2a$12$T94df7ArHkpkX7RGYndcq.fKU.oRlkVLOkCBNrMilaSWnTcWtCfJC; path=/;

Que funciona en todos los navegadores. Luego, para eliminar una cookie, envío una cookie similar con el expirescampo establecido para el 1 de enero de 1970

Set-Cookie: token=$2a$12$T94df7ArHkpkX7RGYndcq.fKU.oRlkVLOkCBNrMilaSWnTcWtCfJC; path=/; expires=Thu, Jan 01 1970 00:00:00 UTC; 

Y eso funciona bien en Firefox pero no elimina la cookie en IE o Safari.

Entonces, ¿cuál es la mejor manera de eliminar una cookie (preferiblemente sin JavaScript)? El método set-the-expires-in-the-past parece voluminoso. ¿Y también por qué funciona esto en FF pero no en IE o Safari?

Joshkunz
fuente
Ver también stackoverflow.com/a/20320610/212378
Alexis Wilke

Respuestas:

208

Enviar el mismo valor de cookie con el ; expiresadjunto no destruirá la cookie.

Invalide la cookie estableciendo un valor vacío e incluya también un expirescampo:

Set-Cookie: token=deleted; path=/; expires=Thu, 01 Jan 1970 00:00:00 GMT

Tenga en cuenta que no puede forzar a todos los navegadores a eliminar una cookie. El cliente puede configurar el navegador de tal manera que la cookie persista, incluso si ha caducado. Establecer el valor como se describe anteriormente resolvería este problema.

Lekensteyn
fuente
52
Recomendaría usar un texto vacío como basura, en lugar de "deleted", para evitar confusiones más adelante con un valor potencialmente legal igual a "eliminado"
yegor256
8
@raulk Sí, tienes razón. Es curioso que no se haya notado antes, es de esperar que no haya causado demasiados problemas. yegor256, un valor vacío debería funcionar en la mayoría de los casos. Relacionado: algunas personas pueden preguntarse por qué sus cookies no se eliminan incluso después de enviar este encabezado. En ese caso, eche un vistazo a las cookies de otros dominios. Por ejemplo, después de eliminar foo=bar; domain=www.example.com, se foo=qux; domain=.example.comusará otra cookie .
Lekensteyn
3
"El cliente puede configurar el navegador de tal manera que la cookie persista, incluso si ha caducado. Establecer el valor como se describe anteriormente resolvería este problema". ¿No podría el cliente configurar el navegador para ignorar su solicitud de configurar el contenido de la cookie como "eliminado" también? No tiene forma de obligar al cliente a hacer algo que no quiere.
Ajedi32
@ Ajedi32 Podría, pero luego debe realizar un esfuerzo adicional para hacerlo (como cliente). El comportamiento de ignorar un valor vacío es mucho más común, no tendría sentido que un navegador ignore tales solicitudes, especialmente para las ID de sesión que se invalidan.
Lekensteyn
2
-1 porque nunca he visto una forma de configurar un navegador para ignorar la caducidad de las cookies, y no estoy convencido de que exista un navegador que ofrezca dicha opción. Además, la primera oración de su respuesta, después de la edición bastante audaz de @ DaveJarvis, ahora es completamente falsa para cualquier navegador importante o cualquier agente de usuario que cumpla con las especificaciones. tools.ietf.org/search/rfc6265#section-5.3 dicta que "El agente de usuario DEBE expulsar todas las cookies caducadas de la tienda de cookies si, en cualquier momento, existe una cookie caducada en la tienda de cookies". y que yo sepa, eso es lo que de hecho hace cada navegador.
Mark Amery
46

Al momento de escribir esta respuesta, la respuesta aceptada a esta pregunta parece indicar que los navegadores no están obligados a eliminar una cookie cuando reciben una cookie de reemplazo cuyo Expiresvalor está en el pasado. Esa afirmación es falsa. La configuración Expirespara estar en el pasado es la forma estándar y compatible con las especificaciones de eliminar una cookie, y las especificaciones requieren que los agentes de usuario la respeten.

Usar un Expiresatributo en el pasado para eliminar una cookie es correcto y es la forma de eliminar las cookies dictadas por la especificación. La sección de ejemplos de RFC 6255 establece:

Finalmente, para eliminar una cookie, el servidor devuelve un encabezado Set-Cookie con una fecha de vencimiento en el pasado. El servidor tendrá éxito al eliminar la cookie solo si la ruta y el atributo de dominio en el encabezado Set-Cookie coinciden con los valores utilizados cuando se creó la cookie.

La sección Requisitos de agente de usuario incluye los siguientes requisitos, que en conjunto tienen el efecto de que una cookie debe ser eliminada de inmediato si el agente de usuario recibe una nueva cookie con el mismo nombre cuya fecha de vencimiento es anterior

  1. Si [al recibir una nueva cookie] la tienda de cookies contiene una cookie con el mismo nombre, dominio y ruta que la cookie recién creada:

    1. ...
    2. ...
    3. Actualice el tiempo de creación de la cookie recién creada para que coincida con el tiempo de creación de la cookie anterior.
    4. Retire la cookie antigua de la tienda de galletas.
  2. Inserte la cookie recién creada en la tienda de cookies.

Una cookie está "vencida" si la cookie tiene una fecha de vencimiento en el pasado.

El agente de usuario DEBE expulsar todas las cookies caducadas de la tienda de cookies si, en cualquier momento, existe una cookie caducada en la tienda de cookies.

Los puntos 11-3, 11-4 y 12 anteriores juntos significan que cuando se recibe una nueva cookie con el mismo nombre, dominio y ruta, la cookie anterior debe ser eliminada y reemplazada por la nueva cookie. Finalmente, el siguiente punto sobre las cookies caducadas dicta que, una vez hecho esto, la nueva cookie también debe ser desalojada de inmediato. La especificación no ofrece margen de maniobra para los navegadores en este punto; Si un navegador ofreciera al usuario la opción de deshabilitar la caducidad de las cookies, como la respuesta aceptada sugiere que algunos navegadores lo hacen, sería una violación de la especificación. (Tal característica también tendría poco uso, y que yo sepa, no existe en ningún navegador).

¿Por qué, entonces, el OP de esta pregunta observó que este enfoque fallaba? Aunque no he desempolvado una copia de Internet Explorer para verificar su comportamiento, ¡sospecho que fue porque el Expiresvalor del OP estaba mal formado! Usaron este valor:

expires=Thu, Jan 01 1970 00:00:00 UTC;

Sin embargo, esto es sintácticamente inválido de dos maneras.

La sección de sintaxis de la especificación dicta que el valor del Expiresatributo debe ser un

rfc1123 -date, definido en [RFC2616], Sección 3.3.1

Siguiendo el segundo enlace anterior, encontramos esto como un ejemplo del formato:

Sun, 06 Nov 1994 08:49:37 GMT

y encuentre que la definición de sintaxis ...

  1. requiere que las fechas se escriban en formato de día, mes y año , no en formato de día, día y año , tal como lo utiliza el autor de la pregunta

    Específicamente, define rfc1123-datelo siguiente:

    rfc1123-date = wkday "," SP date1 SP time SP "GMT"
    

    y define date1así:

    date1        = 2DIGIT SP month SP 4DIGIT
                 ; day month year (e.g., 02 Jun 1982)
    

y

  1. no permite UTCcomo zona horaria.

    La especificación contiene la siguiente declaración sobre qué compensaciones de zona horaria son aceptables en este formato:

    Todas las marcas de fecha / hora HTTP DEBEN estar representadas en la hora media de Greenwich (GMT), sin excepción.

    Además, si profundizamos en la especificación original de este formato de fecha y hora, encontramos que en su especificación inicial en https://tools.ietf.org/html/rfc822 , la sección Sintaxis enumera "UT" (que significa "hora universal" ) como un valor posible, pero no enumera no UTC (Tiempo Universal Coordinado) como válido. Hasta donde yo sé, usar "UTC" en este formato de fecha nunca ha sido válido; no era un valor válido cuando el formato se especificó por primera vez en 1982, y la especificación HTTP adoptó una versión estrictamente más restrictiva del formato al prohibir el uso de todos los valores de "zona" que no sean "GMT".

Si el autor de la pregunta aquí hubiera utilizado un Expiresatributo como este , entonces:

expires=Thu, 01 Jan 1970 00:00:00 GMT;

entonces probablemente habría funcionado.

Mark Amery
fuente
15

Establecer "caduca" en una fecha pasada es la forma estándar de eliminar una cookie.

Su problema probablemente se deba a que el formato de fecha no es convencional. IE probablemente solo espera GMT.

irreputable
fuente
2

Use Max-Age = -1 en lugar de "Expires". Es más corto, menos exigente con la sintaxis, y Max-Age tiene prioridad sobre Expires de todos modos.

Steven Pemberton
fuente
-1

Para la implementación de GlassFish Jersey JAX-RS, he resuelto este problema mediante un método común que describe todos los parámetros comunes. Al menos tres de los parámetros deben ser iguales: nombre (= "nombre"), ruta (= "/") y dominio (= nulo):

public static NewCookie createDomainCookie(String value, int maxAgeInMinutes) {
    ZonedDateTime time = ZonedDateTime.now().plusMinutes(maxAgeInMinutes);
    Date expiry = time.toInstant().toEpochMilli();
    NewCookie newCookie = new NewCookie("name", value, "/", null, Cookie.DEFAULT_VERSION,null, maxAgeInMinutes*60, expiry, false, false);
    return newCookie;
}

Y úselo de la manera común para configurar cookies:

NewCookie domainNewCookie = RsCookieHelper.createDomainCookie(token, 60);
Response res = Response.status(Response.Status.OK).cookie(domainNewCookie).build();

y para eliminar la cookie:

NewCookie domainNewCookie = RsCookieHelper.createDomainCookie("", 0);
Response res = Response.status(Response.Status.OK).cookie(domainNewCookie).build();
RoutesMaps.com
fuente
para mí cuando configuro maxAge en 0, genera una cookie con Max-Age = 0 que Chrome parece ignorar. En RFC 6265 sección 4.1.1 especifica la sintaxis de Max-Age como "dígitos que no son cero". Esa podría ser la razón. Aunque, como lo menciona @ JoshC13, la sección 5.2.2 habla sobre la interpretación de valores menores o iguales a cero. Así que se contradice allí ...
Matthijs Wessels
No conozco detalles, pero estos valores en pareja realmente funcionan en Chrome y otros navegadores: maxAgeInMinutes * 60, caducidad.
RoutesMaps.com
1
@MatthijsWessels ¡Buena captura! Cavé un poco más profundo, y la aparente contradicción es de hecho intencional, como se señala en la errata en rfc-editor.org/errata/eid3430 . Para "maximizar la interoperabilidad", los agentes de usuario deben interpretar un cero o negativo Max-Agecomo la fecha y la hora representables más antiguas, pero los servidores tienen prohibido enviar dicho Max-Agevalor. Supongo que los autores sabían tanto de los clientes existentes que no podían manejar Max-Age=0como de los servidores que lo enviaron en el momento en que escribieron la especificación, e intentaron mitigar el problema desde ambos extremos.
Mark Amery
@ Crimean.us Yo tampoco puedo reprobar más. Tal vez hice algo mal
Matthijs Wessels
@MatthijsWessels El problema con ignorar Max-Age = 0 se soluciona en mi ejemplo al establecer la fecha de vencimiento en ZonedDateTime.now (). PlusMinutes (maxAgeInMinutes). Para maxAgeInMinutes = 0 es la fecha y hora actual. Este código funciona desde hace mucho tiempo en la aplicación web real.
RoutesMaps.com