Instrucciones para un botón que no estaba
El 31 de agosto de 2026, 163 páginas del Portal de la Administración Presupuestaria llevaban dentro las instrucciones de un botón de notificaciones urgentes. Si la página no estaba abierta en una dirección de dentro, el botón tenía que mandar a una máquina de una red interna. Ninguna de las páginas que guardamos tenía ese botón, y el 21 de septiembre las instrucciones ya no estaban.
- Dominio
- www.pap.hacienda.gob.es
- Guardado
- 31 AGO 2026
- Citas
- 6 en 1 fichero guardado
- Lectura
- unos 3 min

El trozo de programa iba escrito dentro de la propia página y traía varias funciones, órdenes con nombre que la página ejecuta cuando alguien pulsa algo. Una se llama abrirNotificacionesUrgentes, y su nombre no deja lugar a dudas. Lo primero que hace es leer el nombre del sitio en el que está abierta la página, la parte de la dirección que va después de las dos barras. En el portal público, ese nombre es www.pap.hacienda.gob.es.

- Copia
- archivo
- Guardado
- 31 ago 2026
- Peso
- 4,8 kB
- sha256
- 2d7741066cd0…
| 56 | fetch(url |
| 57 | .then(res => res.json()) |
| 58 | .then(res => { |
| 59 | let datos = JSON.parse(res.d); |
| 60 | |
| 61 | console.log(datos); |
| 62 | }) |
| 63 | .catch(err => console.error('Error:', err)); |
| 64 | } |
| 65 | function abrir |
| 66 | { |
| 67 | const host = window.location.hostname; |
| 68 | let url = 'http://' + host + '/ |
| 69 | if(host.includes('igae') || host.includes('internet')) |
| 70 | { |
| 71 | window.open(url,'_blank'); |
| 72 | } |
| 73 | else |
| 74 | { |
| 75 | url = 'http:// |
| 76 | if(host.includes('pre')) |
| 77 | { |
| 78 | url = 'http:// |
Con ese nombre arma una dirección: http, el nombre del sitio y detrás la ruta de una lista de SharePoint llamada Notificaciones Urgentes. Y entonces pregunta. Si el nombre del sitio contiene la palabra igae o la palabra internet, abre esa dirección en una pestaña nueva. Si no, la cambia por otra escrita a mano: una máquina cuyo nombre el museo no publica, dentro del dominio central.sepg.minhac.age. Y si además el nombre del sitio contiene pre, la máquina es otra, con pre delante de su nombre.
- Copia
- archivo
- Guardado
- 31 ago 2026
- Peso
- 4,8 kB
- sha256
- 2d7741066cd0…
- Tapado
- 2 tramos ▒
| 58 | .then(res => { |
| 59 | let datos = JSON.parse(res.d); |
| 60 | |
| 61 | console.log(datos); |
| 62 | }) |
| 63 | .catch(err => console.error('Error:', err)); |
| 64 | } |
| 65 | function abrir |
| 66 | { |
| 67 | const host = window.location.hostname; |
| 68 | let url = 'http://' + host + '/ |
| 69 | if(host.includes('igae')2 || host.includes('internet')) |
| 70 | { |
| 71 | window.open(url,'_blank'); |
| 72 | } |
| 73 | else |
| 74 | { |
| 75 | url = 'http:// |
| 76 | if(host.includes('pre')4) |
| 77 | { |
| 78 | url = 'http:// |
| 79 | } |
| 80 | window.open(url,'_blank'); |
| 81 | } |
| 82 | |
| 83 | } |
| 84 | |
| 85 | var head = document. |
| 86 | var styleTag = document. |
| 87 | styleTag.innerText = '#ID_GetLink {display:none;}' |
Ese sufijo, .age, es la firma de todo esto. No es un final de dominio de internet: no está en la lista de los que se reparten, y si se pregunta por él a la red, la respuesta es que ese nombre no existe. Solo tiene sentido dentro de una red que tenga sus propios servidores de nombres, los que traducen el nombre de una máquina a su dirección. Fuera de una red así no lleva a ninguna parte.
Sumadas las ramas, quien tuviera la página abierta en un sitio con igae o internet en el nombre abría la lista de ese mismo sitio. Quien la tuviera abierta en el portal público, que es la puerta de todos los demás, iba a la máquina interna, y desde fuera de esa red su navegador no habría encontrado la dirección. Habría, en condicional, porque para eso alguien tenía que pulsar el botón, y el botón no estaba.
En las 186 páginas de ese sitio que guardamos con respuesta normal, la función estaba escrita en 163, y nada la llamaba: ningún botón, ningún enlace, ninguna otra línea de programa. Tampoco en nada de lo que guardamos de hacienda.gob.es. Eran las instrucciones de un botón que el portal público no tenía, y viajaban con cada página igual que el resto del trozo de programa, con los nombres de dos máquinas de dentro escritos en ellas.
No había ningún secreto detrás, y hay que decirlo porque es fácil imaginarlo. Nadie entraba en ninguna parte: nadie pulsaba nada, y el nombre no se resuelve desde fuera. Lo que quedaba a la vista era el mapa, dos máquinas de dentro con su dominio, en las páginas de fuera. Por eso el museo tapa sus nombres y deja el dominio: el dominio cuenta el caso; los nombres solo servirían para orientarse dentro.
El trozo de programa traía más cosas que nadie llamaba. Una función mandaba la dirección de la página a un servicio de accesibilidad del propio portal y escribía la respuesta entera en la consola del navegador, la ventana de avisos para quien programa, con console.log, la orden que se usa para depurar. Otras dos copiaban una dirección al portapapeles con document.execCommand, una manera que los navegadores dan por obsoleta, y avisaban con una ventanita: «Copiada url al portapapeles».
- Copia
- archivo
- Guardado
- 31 ago 2026
- Peso
- 4,8 kB
- sha256
- 2d7741066cd0…
| 50 | headers: new Headers({ |
| 51 | 'Accept': 'application/json', |
| 52 | 'Content-Type': 'application/json' |
| 53 | }) |
| 54 | }; |
| 55 | const urlPortalObservatorio = window.location.href + '/_ |
| 56 | fetch(url |
| 57 | .then(res => res.json()) |
| 58 | .then(res => { |
| 59 | let datos = JSON.parse(res.d); |
| 60 | |
| 61 | console.log(datos); |
| 62 | }) |
| 63 | .catch(err => console.error('Error:', err)); |
| 64 | } |
| 65 | function abrir |
| 66 | { |
| 67 | const host = window.location.hostname; |
| 68 | let url = 'http://' + host + '/ |
| 69 | if(host.includes('igae') || host.includes('internet')) |
| 70 | { |
| 71 | window.open(url,'_blank'); |
| 72 | } |
| 30 | function copiar |
| 31 | { |
| 32 | const body = document. |
| 33 | const area = document. |
| 34 | body.appendChild(area); |
| 35 | const lugar = urlSitio.split('/').length -1; |
| 36 | const sitio = urlSitio.split('/')[lugar]; |
| 37 | const urlParcial = url. |
| 38 | area.value = urlSitio.replace(sitio,'') + urlParcial; |
| 39 | area.select(); |
| 40 | document. |
| 41 | alert('Copiada url al portapapeles: ' + area.value); |
| 42 | body.removeChild(area); |
| 43 | } |
| 44 | function analizarPorObservatorio() |
| 45 | { |
| 46 | const urlLoc = {'url': |
| 47 | let fetchData = { |
| 48 | method: 'POST', |
| 49 | body: JSON.stringify(urlLoc), |
| 50 | headers: new Headers({ |
| 51 | 'Accept': 'application/json', |
| 52 | 'Content-Type': 'application/json' |
Y luego cambió. El 15 de septiembre de 2026 la página seguía igual. El 21 de septiembre la volvimos a pedir y ya no traía la función del botón, ni las otras tres, ni la dirección de la lista. El 26, tampoco. Queda el nombre del dominio interno: tres veces, en una lista de direcciones de dentro que el propio SharePoint escribe en cada página, con los nombres de otras tres máquinas que tampoco publicamos.
Las instrucciones de un botón que el portal público no tenía viajaron en 163 páginas, con los nombres de dos máquinas de dentro. El 21 de septiembre ya no viajaban.
- En 163 de las 186 páginas de www.pap.hacienda.gob.es guardadas el 31 de agosto de 2026 con respuesta normal había un trozo de programa con la función
abrirNotificacionesUrgentes: lee el nombre del sitio en el que está abierta la página y, si no contiene igae ni internet, cambia la dirección de la lista Notificaciones Urgentes por la de un servidor del dominio central.sepg.minhac.age; si contiene pre, por la de otro servidor del mismo dominio. - El sufijo .age no está en la lista de dominios de primer nivel de la IANA, consultada el 26 de septiembre de 2026.
- En todo lo que guardamos de hacienda.gob.es no hay ninguna llamada a esa función ni a las otras tres del mismo trozo (una con console.log y dos que copian al portapapeles con document.
execCommand). - El nombre del dominio interno aparecía en 166 de las 189 páginas .aspx guardadas de ese host.
- El 15 de septiembre de 2026 la página seguía igual; el 21 y el 26 de septiembre ya no traía esas cuatro funciones, y el dominio interno aparecía tres veces, en la lista de direcciones internas que escribe SharePoint.
- No decimos que a esas máquinas se pueda llegar desde fuera: el nombre no se resuelve en internet y no hemos intentado alcanzarlas.
- No publicamos el nombre de ninguna de ellas.
- No decimos que el portal público tuviera un botón que llamara a esa función: en lo que guardamos no lo hay, y no sabemos si lo hay en la intranet.
- No decimos qué había al otro lado ni por qué se quitó el código: no consta en nada que tengamos.
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.pap.hacienda.gob.es/sitios/pap/es-ES/Paginas/InformacionProteccionDatos.aspx31 de agosto de 2026, 21:45archivosha256 2d7741066cd001bb99f340ac9e3a65a6dc4f46692862ed9969e12461462598c5
El 21 de septiembre de 2026, 6 de sus 6 citas ya no aparecían en la página. Lo que ha cambiado desde que lo contamos.