Una galleta que no se borraba
Hasta el 16 de septiembre de 2026, quien entraba en la web del Ministerio de Sanidad se llevaba, con la portada, un fichero de noventa líneas para manejar las cookies. Estaba escrito en español, las cookies eran galletas, y comentado paso a paso. Su función para borrar una galleta, si alguien la llamaba, paraba en seco el programa: lo ejecutamos para asegurarnos. Ese día, según el propio servidor, el fichero cambió y la función quedó arreglada.
- Dominio
- www.sanidad.gob.es
- Guardado
- 31 AGO 2026
- Comprobado en vivo
- 21 SEP 2026
- Citas
- 7 en 2 ficheros guardados
- Lectura
- unos 4 min
Lo primero que llama la atención es que se entiende. Cada línea lleva su comentario en español explicando qué hace y por qué, la variable donde se vuelca el contenido de la cookie se llama galleta, y los comentarios conservan las erratas de quien los escribió con prisa: «si en la cookie estamo ya en nombre del valor», «devuleve el valor». El servidor lo daba por modificado por última vez el 14 de marzo de 2017, y así lo guardamos el 31 de agosto y lo volvimos a pedir el 15 de septiembre, idéntico.

- Copia
- archivo
- Guardado
- 31 ago 2026
- Peso
- 2,6 kB
- sha256
- d2979066a147…
| 2 | function cojerValorCookie(indice) { |
| 3 | //indice indica el comienzo del valor |
| 4 | var galleta = document.cookie; |
| 5 | //busca el final del valor, dado por ;, a partir de indice |
| 6 | var finDeCadena = galleta.indexOf(";", indice); |
| 7 | //si no existe el ;, el final del valor lo marca la longitud total de la cookie |
| 8 | if (finDeCadena == -1) |
| 9 | finDeCadena = galleta.length; |
| 10 | |
| 11 | return unescape(galleta. |
| 12 | } |
| 13 | |
| 14 | function cojerCookie(nombre) { |
| 15 | var galleta = document.cookie; |
| 14 | function cojerCookie(nombre) { |
| 15 | var galleta = document.cookie; |
| 16 | //construye la cadena con el nombre del valor |
| 17 | var arg = nombre + "="; |
| 18 | var alen = arg.length; //longitud del nombre del valor |
| 19 | var glen = galleta.length; //longitud de la cookie |
| 20 | |
| 21 | var i = 0; |
| 22 | while (i < glen) { |
| 23 | var j = i + alen //posiciona j al final del nombre del valor |
| 24 | if (galleta.substring(i, j) == arg) //si en la cookie estamo ya en nombre del valor |
| 25 | return cojerValorCookie(j) //devuleve el valor, que esta a partir de j |
| 26 | |
| 27 | i = galleta.indexOf(" ", i) + 1 //pasa al siguiente |
| 28 | if (i == 0) |
| 29 | break //fin de la cookie |
| 30 | } |
| 31 | return null //no se encuentra el nombre del valor |
| 32 | } |
| 33 | |
| 34 | function guardarCookie (nombre, valor, caducidad) { |
| 35 | if(!caducidad) |
| 36 | caducidad = caduca(0); |
La función que calcula cuándo caduca una galleta recibía un número de días y lo pasaba a milisegundos. Multiplicar por 24, por 60, por 60 y por 1.000 no tiene misterio. Lo tenía la orden que iba delante. Se llama eval y sirve para coger un texto y ejecutarlo como si fuera programa. Aquí se usaba para leer un número de días antes de multiplicarlo por veinticuatro, como quien pide a un intérprete que traduzca un número de teléfono.
- Copia
- archivo
- Guardado
- 31 ago 2026
- Peso
- 2,6 kB
- sha256
- d2979066a147…
| 34 | function guardarCookie (nombre, valor, caducidad) { |
| 35 | if(!caducidad) |
| 36 | caducidad = caduca(0); |
| 37 | |
| 38 | //crea la cookie: incluye el nombre, la caducidad y la ruta donde esta guardada |
| 39 | //cada valor esta separado por ; y un espacio |
| 40 | document.cookie = nombre + "=" + escape(valor) + "; expires=" + caducidad + "; path=/" |
| 41 | } |
| 42 | |
| 43 | function caduca(dias) { |
| 44 | var hoy = new Date(); //coge la fecha actual |
| 45 | var msEnXDias = eval(dias)*24*60*60*1000; //pasa los dias a mseg. |
| 46 | hoy.setTime(hoy.getTime() + msEnXDias); //fecha de caducidad: actual + caducidad |
| 47 | return (hoy.toGMTString()); |
| 48 | } |
| 49 | |
| 50 | function borrarCookie(nombre) { |
| 51 | //para borrar la cookie, se le pone una fecha del pasado mediante Caduca(-1) |
| 52 | document.cookie = nombre + "=; expires=" + Caduca(-1) + "; path=/" |
| 53 | } |
| 54 | |
| 55 | function introducirCookie(parametro, valor) { |
| 56 | //establece la cookie, caducidad 90 días |
Lo comprobamos ejecutando el fichero tal como lo servía el ministerio. Si a esa función se le pedía que caducara en 90 días, contestaba con la fecha de dentro de noventa días, correctamente. Si se le pedía que caducara en “2*15”, un texto y no un número, contestaba con la de dentro de treinta. La multiplicación la hacía el eval, que ejecuta como programa el texto que le llegue, sea una cuenta o cualquier otra orden.
Unas líneas más abajo estaba la función que establece la galleta con su caducidad, y ahí convivían dos comentarios y un número. El comentario de arriba anunciaba noventa días de caducidad. El código pedía noventa días. Y el comentario de la derecha, en la misma línea, decía que la fecha se creaba para treinta y uno. No sabemos cuál de los dos sobrevivió a cuál. Sabemos que el que manda es el número, y que un lector desprevenido tenía una posibilidad entre dos de creerse el equivocado.
- Copia
- archivo
- Guardado
- 31 ago 2026
- Peso
- 2,6 kB
- sha256
- d2979066a147…
| 46 | hoy.setTime(hoy.getTime() + msEnXDias); //fecha de caducidad: actual + caducidad |
| 47 | return (hoy.toGMTString()); |
| 48 | } |
| 49 | |
| 50 | function borrarCookie(nombre) { |
| 51 | //para borrar la cookie, se le pone una fecha del pasado mediante Caduca(-1) |
| 52 | document.cookie = nombre + "=; expires=" + Caduca(-1) + "; path=/" |
| 53 | } |
| 54 | |
| 55 | function introducirCookie(parametro, valor) { |
| 56 | //establece la cookie, caducidad 90 días |
| 57 | var _dias = caduca(90); //crea la fecha de caducidad si 31 dias |
| 58 | if (valor != "") |
| 59 | guardarCookie(parametro, valor, _dias); |
| 60 | } |
| 61 | |
| 62 | |
| 63 | var parametro = 'idUsuario'; |
| 64 | |
| 65 | var reply0 = function(data) { |
| 66 | if (data != null && typeof data == 'object') { |
| 67 | |
| 68 | } else { |
Y llegamos al motivo de esta pieza. Para borrar una galleta, el truco de toda la vida es ponerle una fecha de caducidad del pasado: el navegador la tira. El comentario lo explicaba con esas palabras y el código lo hacía llamando a Caduca, con mayúscula, cuando la función que existe se llama caduca, con minúscula. JavaScript, el lenguaje en que está escrito el fichero, distingue mayúsculas. Para él son dos nombres distintos, y el segundo no existía.
- Copia
- archivo
- Guardado
- 31 ago 2026
- Peso
- 2,6 kB
- sha256
- d2979066a147…
| 41 | } |
| 42 | |
| 43 | function caduca(dias) { |
| 44 | var hoy = new Date(); //coge la fecha actual |
| 45 | var msEnXDias = eval(dias)*24*60*60*1000; //pasa los dias a mseg. |
| 46 | hoy.setTime(hoy.getTime() + msEnXDias); //fecha de caducidad: actual + caducidad |
| 47 | return (hoy.toGMTString()); |
| 48 | } |
| 49 | |
| 50 | function borrarCookie(nombre) { |
| 51 | //para borrar la cookie, se le pone una fecha del pasado mediante Caduca(-1) |
| 52 | document.cookie = nombre + "=; expires=" + Caduca(-1) + "; path=/" |
| 53 | } |
| 54 | |
| 55 | function introducirCookie(parametro, valor) { |
| 56 | //establece la cookie, caducidad 90 días |
| 57 | var _dias = caduca(90); //crea la fecha de caducidad si 31 dias |
| 58 | if (valor != "") |
| 59 | guardarCookie(parametro, valor, _dias); |
| 60 | } |
| 61 | |
| 62 | |
| 63 | var parametro = 'idUsuario'; |
Cargamos el fichero en un navegador y pedimos borrar una galleta. La respuesta fue “ReferenceError: Caduca is not defined”, que es la manera que tiene JavaScript de decir que no sabe de quién le hablan. El programa se detiene ahí y la galleta se queda donde estaba, tan tranquila. Buscamos después esa palabra con mayúscula en todo lo que habíamos guardado del ministerio, por si alguien la definía en otro fichero y el asunto quedaba en un susto. No aparecía en ninguna parte.
Lo que salvaba al fichero era lo único que podía salvarlo: nadie llamaba a esa función. El resto sí se usaba, y guardarCookie y caduca se llaman desde otros ficheros del ministerio. En todo lo que guardamos no hay una sola línea que pida borrar una galleta. El error estaba ahí, como poco desde la fecha de marzo de 2017 que daba el servidor, esperando a un programador que un día decidiera usar la función que el fichero le ofrecía. Mientras tanto, Sanidad guardaba y renovaba sin problema.
Queda el detalle de las dos funciones con las que el fichero mete y saca el valor de la galleta, escape y unescape. Son de la primera época de JavaScript y desde 1999 la norma del lenguaje solo las conserva en el anexo de compatibilidad, con la recomendación expresa de no usarlas en código nuevo. Este no era nuevo: el 31 de agosto y el 15 de septiembre el servidor lo entregaba con la misma huella, los mismos finales de línea de Windows y los mismos 2.593 bytes. La versión corregida las sigue usando.
- Copia
- archivo
- Guardado
- 31 ago 2026
- Peso
- 2,6 kB
- sha256
- d2979066a147…
| 2 | function cojerValorCookie(indice) { |
| 3 | //indice indica el comienzo del valor |
| 4 | var galleta = document.cookie; |
| 5 | //busca el final del valor, dado por ;, a partir de indice |
| 6 | var finDeCadena = galleta.indexOf(";", indice); |
| 7 | //si no existe el ;, el final del valor lo marca la longitud total de la cookie |
| 8 | if (finDeCadena == -1) |
| 9 | finDeCadena = galleta.length; |
| 10 | |
| 11 | return unescape(galleta. |
| 12 | } |
| 13 | |
| 14 | function cojerCookie(nombre) { |
| 15 | var galleta = document.cookie; |
| 16 | //construye la cadena con el nombre del valor |
| 17 | var arg = nombre + "="; |
| 18 | var alen = arg.length; //longitud del nombre del valor |
| 19 | var glen = galleta.length; //longitud de la cookie |
| 20 | |
| 21 | var i = 0; |
| 22 | while (i < glen) { |
El 16 de septiembre de 2026 el fichero cambió. Lo dice el propio servidor, que desde entonces da esa fecha como la de su última modificación, y lo dicen las copias que volvimos a pedir el 21 y el 23. Donde estaba eval hay ahora Number, que convierte un texto en número sin ejecutarlo. borrarCookie llama a caduca con minúscula, en el código y en el comentario. El comentario de los treinta y un días ya no está, y el de los noventa ha perdido la í de «días» por el camino: dice «caducidad 90 d as». El resto, galleta y erratas incluidas, sigue igual.
- Copia
- en vivo
- Leído
- 21 sep 2026
- Peso
- 2,6 kB
- sha256
- 2aa99fd66fe2…
| 41 | } |
| 42 | |
| 43 | function caduca(dias) { |
| 44 | var hoy = new Date(); //coge la fecha actual |
| 45 | var msEnXDias = Number(dias)*24*60*60*1000; //pasa los dias a mseg. |
| 46 | hoy.setTime(hoy.getTime() + msEnXDias); //fecha de caducidad: actual + caducidad |
| 47 | return (hoy.toGMTString()); |
| 48 | } |
| 49 | |
| 50 | function borrarCookie(nombre) { |
| 51 | //para borrar la cookie, se le pone una fecha del pasado mediante caduca(-1) |
| 52 | document.cookie = nombre + "=; expires=" + caduca(-1) + "; path=/" |
| 53 | } |
| 54 | |
| 55 | function introducirCookie(parametro, valor) { |
| 56 | //establece la cookie, caducidad 90 d as |
| 57 | var _dias = caduca(90); |
| 58 | if (valor != "") |
| 59 | guardarCookie(parametro, valor, _dias); |
| 60 | } |
| 61 | |
| 62 | |
| 63 | var parametro = 'idUsuario'; |
Noventa líneas que lo explicaban todo y una mayúscula de más en el único sitio donde importaba. El 16 de septiembre se fue la mayúscula, y con ella una tilde.
- El fichero cookie.js que servía www.sanidad.gob.es, con fecha de última modificación del 14 de marzo de 2017 e idéntico el 31 de agosto y el 15 de septiembre de 2026, calculaba la caducidad de sus cookies con eval, tenía en dos líneas seguidas dos comentarios y un número que no coincidían (90, 90 y 31) y su función
borrarCookiellamaba a Caduca con mayúscula cuando la definida era caduca. - Ejecutado ese fichero tal cual, esa llamada devuelve “ReferenceError: Caduca is not defined”; en el archivo del ministerio no hay ninguna definición de Caduca ni ninguna llamada a
borrarCookiefuera del propio fichero, aunqueguardarCookiey caduca sí se llaman desde otros. - Lo cargan las 14 portadas guardadas (siete idiomas, dos direcciones cada uno).
- El servidor da el 16 de septiembre de 2026 como fecha de la última modificación del fichero, y las copias del 21 y del 23 de septiembre llevan Number(dias) y caduca(-1), sin el comentario de los 31 días.
- No decimos que ninguna cookie del ministerio se quedara sin borrar en la práctica: la función rota no la llamaba nadie, y el aviso de cookies de la web puede estar manejado por otro programa que no hemos mirado.
- No decimos que el eval de ese fichero fuera aprovechable por nadie: el número de días lo escribía el propio programa, no el visitante.
- No decimos que el cambio del 16 de septiembre tenga que ver con esta pieza: no lo sabemos.
- Y no hemos probado nada contra el servidor: descargamos el fichero, que es público, y lo ejecutamos en nuestro navegador.
De dónde sale cada cita
Cada trozo de código de esta ficha está en un fichero guardado tal como lo entregó el servidor, con su huella (sha256) y su hora. Las citas entre «» se comprueban por programa contra ese fichero antes de publicar.
- https://www.sanidad.gob.es/diseno/js/cookie.js31 de agosto de 2026, 16:09archivosha256 d2979066a147180e5841d6a21c6fcd3b6921fb741b577c66dff5b29fa369f035
- https://www.sanidad.gob.es/diseno/js/cookie.js15 de septiembre de 2026, 02:27en vivo · byte a byte igualsha256 d2979066a147180e5841d6a21c6fcd3b6921fb741b577c66dff5b29fa369f035
- https://www.sanidad.gob.es/diseno/js/cookie.js21 de septiembre de 2026, 14:23en vivosha256 2aa99fd66fe2efc96f864dca4e3e94751cd6504645b97a3f20c58f9a6c591f93
El 21 de septiembre de 2026, 3 de sus 6 citas ya no aparecían en la página. Lo que ha cambiado desde que lo contamos.