Fundamentos de seguridad
Tu clave API se filtró. El orden en el que hay que actuar
Una clave API se filtró y no sabes por dónde empezar. No todas las claves de tu frontend lo son, y el orden importa más que la velocidad.

En resumen
- Si una clave API se filtró, lo primero que hay que hacer depende de qué clave sea. Las claves publicables van en tu frontend y no necesitan nada.
- Si es un secreto de verdad, averigua si la clave puede gastar dinero. Es la única parte de esto que tiene un reloj corriendo.
- Parar una clave y reemplazar una clave son dos controles distintos en cada proveedor, y casi todos te dan una ventana en la que las dos claves funcionan.
- Después lee el registro del proveedor para el periodo en que la clave estuvo fuera, y deja en paz tu historial de Git hasta que la clave esté muerta.
El aviso suele llegar por un informe de escaneo, por GitHub diciéndote que ha aparecido un secreto en uno de tus repositorios, o por alguien que pulsó F12 en tu sitio y te mandó una captura. Por donde sea que te haya llegado, ya sabes que una clave API tuya se filtró y no sabes por dónde empezar.
Esta es la parte que guía tras guía se equivoca: dan la misma respuesta para cualquier clave, y la respuesta siempre es rotarla. Algunas de esas claves están pensadas para ser públicas y no necesitan nada. De las demás, algunas están engordando una factura mientras lees esto, y otras hicieron todo su daño el día del despliegue. Son tres situaciones distintas, y el primer paso es distinto en cada una.
Primero: ¿esa clave es realmente un secreto?
A menudo no, y los números están lo bastante desequilibrados como para decirlo sin rodeos.
Escaneamos 31.056 aplicaciones en vivo y leímos el JavaScript que cada una envía al navegador. La comprobación que busca credenciales pudo responder en todas, y 1.332 volvieron con al menos una clave que merecía un aviso. 1.142 de ellas eran claves API de Google, que es el único tipo del grupo que suele estar justo donde le corresponde.
Una clave API de Google en una página web es lo que hace funcionar Google Maps. La clave dice qué proyecto paga, y lo que impide que un desconocido gaste a tu costa es la restricción puesta en la clave y no su secreto. La guía de Google es clara en las dos mitades: "Unrestricted API keys are insecure", y "You are financially responsible for charges caused by abuse of unrestricted API keys." El arreglo para una clave así es abrirla en la Cloud Console y añadir dos restricciones, una que nombre tu sitio web y otra que nombre las API que de verdad llamas. La clave en sí nunca cambia.
La otra familia que va en el navegador son las claves publicables. Una clave de
Stripe pk_live_, y una de Supabase sb_publishable_ o anon, están hechas
para que las lea cada visitante que tengas.
Qué claves son seguras en tu frontend
es la versión de cuatro caracteres de esa prueba.
Si prefieres no recorrer tu bundle a mano, nuestro escaneo gratuito lee tu sitio en vivo, enumera las claves que ve desde fuera y dice de qué tipo es cada una: escanea tu aplicación. Tarda unos 20 segundos y no pide cuenta.
Mi clave API se filtró. ¿Qué hago primero?
Averigua si la clave tiene un contador.
Una clave con contador te cobra cada petición. La de Google, la de OpenAI, la de
Anthropic y una clave de AWS con los permisos equivocados detrás son todas
contadores: el tráfico de otra persona aterriza en tu factura, a un ritmo que
elige esa persona y que tú no ves. Una clave sin contador lee o escribe tus
datos en su lugar. Una clave service_role de Supabase es el ejemplo más claro,
y nada en ella cuesta dinero por hora.
Esa única pregunta fija el orden.
Si la clave tiene un contador, párala ahora. La función que la usaba queda rota hasta que despliegues el reemplazo, y ese es el intercambio correcto, porque la factura es la única parte de esto que sigue creciendo mientras planificas.
Si la clave lee datos y se publicó en tu bundle, la lectura ya ocurrió. Cada visitante que cargó la página tiene una copia, y también cada rastreador que fue a buscar exactamente esa cadena. Nada empeora mientras averiguas el orden correcto, y lo que conviene acertar es no quedarte fuera de tu propia aplicación por el camino.
Qué puede hacer realmente cada tipo de clave
| Clave | Gasta dinero | Lee tus datos | ¿Se puede restringir en su lugar? | ¿Reemplazarla rompe la aplicación? |
|---|---|---|---|---|
Supabase sb_publishable_ o anon | No | Solo las filas que tus reglas permiten | Su sitio es el navegador | No aplica |
Stripe pk_live_ | No | No | Su sitio es el navegador | No aplica |
Google AIza… | Sí, en tu factura de Cloud | No | Sí, y Google dice que pruebes eso primero | Restringirla no. Rotarla sí puede. |
| Clave de OpenAI o Anthropic | Sí, al ritmo que marque un extraño | Los archivos y asistentes del proyecto | No existe una variante publicable | Sí, hasta que la tenga un servidor tuyo |
Stripe sk_live_ o rk_live_ | Sí | Sí, clientes y registros de pago | Una clave restringida es la versión estrecha | No, hay una ventana de siete días |
AWS AKIA… y su mitad secreta | Sí, incluido cómputo por hora | Tus buckets de S3 | Deactivate, y es reversible | No, puedes tener dos claves a la vez |
Supabase sb_secret_ o service_role | No | Cada fila de cada tabla | No | Sí, en un proyecto antiguo |
Dos notas sobre esa tabla, porque ambas cambian lo que haces a continuación.
Una clave de acceso de AWS son dos cadenas, un identificador que empieza por
AKIA y una mitad secreta, y AWS exige las dos juntas para firmar una petición.
Así que una cadena AKIA sola en un bundle no se puede usar, y la razón para
tratarla como urgente de todos modos es que las dos mitades casi siempre se
pegan juntas. Abre el archivo y busca la segunda antes de decidir en qué caso
estás.
El detalle de cada proveedor vive con cada proveedor: una clave de OpenAI, una clave API de Google, una clave secreta de Stripe y una clave service_role de Supabase, que es la única con un procedimiento propio.
Parar la clave y reemplazar la clave son dos botones distintos
Cada proveedor de esa tabla te da los dos, y el pánico va a por el segundo.
- Google. Restringir una clave no cambia la cadena, así que tu aplicación sigue funcionando. Su guía de seguridad lo pone por delante de todo lo demás: "First try to restrict your API keys", y rotar es la tercera opción, para cuando una restricción no es posible.
- Stripe. Expire key para una clave por sí sola, sin reemplazo de por medio. Su postura sobre cuándo usarlo no tiene matices: "If a restricted or secret API key is exposed or compromised, rotate it immediately even if you aren't sure anyone saw it." También separan las dos palabras que todo el mundo usa como sinónimos. Exposure es que la clave se haya vuelto visible donde no debía. Compromise es la prueba de que alguien la usó.
- AWS. Deactivate es el control, y lo útil es que se puede deshacer. AWS dice que no borres la clave vieja mientras sigas comprobando: "we recommend that you do not immediately delete the first access key. Instead, choose Actions and then choose Deactivate." Si resulta que algo que olvidaste todavía la necesita, la vuelves a encender.
- Supabase, en un proyecto antiguo. Hay un interruptor, y cubre las dos
claves heredadas.
anonyservice_roleestán firmadas por el mismo secreto, así que desactivar la que quieres matar detiene también la que usa tu frontend. Los cuatro pasos que evitan eso conviene leerlos antes de tocar el interruptor y no después.
Leer el registro del periodo en que la clave estuvo fuera
La ventana se abre con el despliegue que publicó la clave y se cierra cuando la paraste. Ese rango de fechas es el filtro que aplicas al registro del proveedor.
Todos los proveedores de la tabla guardan uno. Stripe muestra los registros de peticiones de una sola clave, desde el menú de tres puntos junto a esa clave en la página de API Keys. AWS pone una fecha de último uso en cada clave de acceso en la consola de IAM sin configurar nada, y guarda las llamadas en sí en CloudTrail. Google grafica el uso por clave en la Cloud Console, que es además la lectura que su propia guía te pide hacer antes de cambiar nada de una clave. Supabase conserva registros de API y de base de datos del proyecto, y cómo acotar la ventana en una clave de Supabase explica qué buscar en ellos.
Lo que buscas es tráfico que no puedas explicar: peticiones a tablas o endpoints que tu aplicación nunca toca, volumen a horas en que nadie la usaba, eliminaciones que nadie hizo. Después mira los datos en sí, porque un mensaje de soporte sobre un registro que cambió solo es como se descubre la mayoría de estos casos, y llega semanas más tarde.
A menudo no puedes estar seguro, y Stripe dice lo mismo de su propia detección: "Stripe doesn't guarantee detection of all exposed or compromised keys." Así que "nada en el registro" es un resultado, y vale la pena anotarlo con el rango de fechas al lado.
Rotar sin dejar la aplicación parada
Tres de los cuatro proveedores de arriba te dan una ventana en la que la clave vieja y la nueva funcionan a la vez, y eso es lo que evita una caída.
Stripe. "When you rotate a key in the Dashboard, both the old and new keys work for up to 7 days." El diálogo también tiene una opción Now, y su documentación es explícita: si la eliges, la clave vieja se borra. Ese es el botón de la emergencia con contador y no el de una migración tranquila. Su consejo sobre cuándo soltar la vieja es una medición y no una fecha: revisa sus registros de peticiones y hazla expirar solo cuando su volumen lleve unas horas o unos días a cero.
Google. Rotar crea la clave nueva con todas las restricciones de la vieja y, en sus palabras, "both the old and new key are accepted" durante la ventana en la que mueves tus aplicaciones. Si borras la vieja demasiado pronto y algo se rompe, hay vuelta atrás: una clave API de Google borrada se puede restaurar dentro de los 30 días.
AWS. La secuencia está en su documentación y empieza por la clave nueva: crea la segunda clave de acceso mientras la primera sigue activa, mueve cada aplicación a ella, revisa la fecha de último uso de la vieja, desactívala y solo entonces bórrala. Un techo con el que contar: un usuario de IAM puede tener como mucho dos claves de acceso, así que una tercera aplicación que siga con una clave vieja no tiene adónde ir.
Supabase, en un proyecto antiguo. Sin ventana. La rotación directa de las
claves heredadas anon y service_role ya no está soportada, así que invalidar
una es una migración al nuevo par de claves, y el orden de los cuatro pasos es
lo que mantiene la aplicación en pie.
Sigue estando en tu historial de Git
Lo está, y la documentación de GitHub sobre eso empieza mandándote a otro sitio.
Su página sobre eliminar datos sensibles te devuelve a la clave: "if the sensitive data you need to remove is a secret (e.g. password/token/credential), as is often the case, then as a first step you need to revoke and/or rotate that secret", y después: "Once the secret is revoked or rotated, it can no longer be used for access, and that may be sufficient to solve your problem. Going through the extra steps to rewrite the history and remove the secret may not be warranted."
La razón que dan es lo que un force push no alcanza. Después de reescribir tu historial, los commits viejos siguen ahí "In any clones or forks of your repository" y "Directly via their SHA-1 hashes in cached views on GitHub". El soporte puede limpiar las vistas en caché y las referencias en pull requests si lo pides, y marca su propia raya: "will only assist in the removal of sensitive data in cases where we determine that the risk can't be mitigated by rotating affected credentials." Un fork se queda con su copia en cualquier caso, y GitHub no puede darte los datos de contacto de su dueño.
Así que el orden que describen es el práctico: revocar la clave y después decidir si reescribir el historial compensa sus efectos secundarios. Revocar alcanza copias que una reescritura no alcanza, incluida la de un clon del que no sabes nada y la de una captura que alguien guardó.
Que la siguiente no entre en el bundle
Una clave entra en tu bundle por un despliegue, y los despliegues siguen pasando. Cada uno es otra oportunidad de que un valor que pusiste en un panel de secretos acabe en un archivo que el navegador descarga. Por eso una comprobación hecha el mes pasado describe la aplicación del mes pasado.
Nuestro escaneo gratuito responde a la pregunta con la que llegaste.
- las nueve comprobaciones contra tu URL en vivo, en unos 20 segundos
- cada clave que ve desde fuera, clasificada y no solo encontrada
- una nota y los hallazgos, sin cuenta
Reeve Monitor repite esas comprobaciones sin que lo pidas.
- las nueve comprobaciones cada hora, en hasta tres aplicaciones
- un mensaje cuando un resultado cambia, para que la clave que salió en el despliegue de anoche no espere a que mires
- si la aplicación responde, cada 60 segundos
- un informe mensual de lo que vio
Reeve Care guarda una copia de tu base de datos de Supabase, para las claves que pueden escribir.
- una copia cifrada cada noche, guardada donde tu proyecto no llega
- cada copia verificada antes de contar, contando las filas de cada tabla
- una restauración con un clic cuando la necesites
- también tus archivos subidos, en cuanto conectes una credencial de Storage
- todo lo que hace Monitor
Una clave filtrada que solo puede leer deja tus datos donde estaban. Una clave
service_role, o una de AWS con permisos de escritura, puede vaciar una tabla,
y por mucho que rotes después las filas no vuelven.
El día en que un agente de IA borró una base de datos de producción
cuenta cómo se ve eso desde dentro.
Qué hacer ahora mismo
Qué hacer
- Identifica la clave antes de tocarla. Una clave
anonosb_publishable_de Supabase y unapk_live_de Stripe están donde deben estar, y de las 1.332 aplicaciones en las que encontramos una clave que merecía aviso, 1.142 tenían una clave API de Google, que pide una restricción en lugar de una rotación. - Pregúntate si tiene un contador. Si las peticiones de otra persona aterrizan en tu factura, para la clave ahora y deja que la función se rompa.
- Usa el control que para igual que el que reemplaza: Expire key en Stripe, Deactivate en AWS, una restricción por sitio y por API en Google.
- Reemplázala después dentro de la ventana del proveedor. Stripe te da siete días con las dos claves activas, Google acepta ambas mientras migras, y AWS te deja tener dos claves de acceso a la vez.
- Lee el registro del proveedor para el periodo entre el despliegue y la parada, y anota lo que encontraste, incluido "nada".
- Deja el historial de Git para el final. Una vez revocada la clave, la propia guía de GitHub dice que reescribir el historial puede no merecer la pena en absoluto.
Si prefieres recorrer todo esto como una lista, la checklist de seguridad en 10 minutos cubre esto y las otras cosas que conviene apagar en una aplicación recién lanzada.
Preguntas frecuentes
Mi clave API se filtró. ¿Qué hago primero?
Averigua si la clave puede gastar dinero. Una clave que te cobra por petición te está costando algo ahora mismo, así que párala de inmediato y asume que la función que la usa se rompe unos minutos. Una clave que solo lee datos ya ha sido leída si era pública, así que tómate el tiempo de hacer el reemplazo en un orden que no te deje fuera de tu propia aplicación.
¿Revocar o rotar primero?
Revocar primero si la clave tiene un contador. Parar la clave vieja y emitir una nueva son dos controles separados en cada proveedor, y solo el primero cierra el agujero. La excepción es una clave que puedes restringir en su lugar: Google te dice que pruebes una restricción por sitio y por API antes de rotar una clave API de Google siquiera.
¿Cómo sé si alguien usó mi clave?
Reduce la ventana al periodo entre el despliegue que publicó la clave y el momento en que la paraste, y lee el registro del proveedor para ese rango. Stripe muestra registros de peticiones por clave, AWS muestra una fecha de último uso en la clave de acceso y guarda las llamadas en CloudTrail, Google grafica el uso por clave, y Supabase conserva registros de API y de base de datos. Buscas peticiones a cosas que tu aplicación nunca toca y tráfico a horas en que nadie la estaba usando. A menudo no puedes estar seguro, y ese es un resultado normal.
¿Rotar la clave romperá mi aplicación?
Normalmente no, si usas la ventana que te da el proveedor. Stripe mantiene la clave vieja y la nueva funcionando juntas hasta siete días. Google emite la clave nueva con las restricciones de la vieja y acepta ambas mientras migras. AWS te deja tener dos claves de acceso a la vez, así que la nueva ya está activa antes de que se vaya la vieja. La excepción es un proyecto antiguo de Supabase, donde el interruptor que desactiva las claves heredadas se lleva por delante la clave de tu frontend.
La clave está en mi historial de Git. ¿Basta con borrar el archivo?
No, y reescribir el historial probablemente tampoco sea la respuesta. La documentación de GitHub dice que revoques o rotes el secreto primero, y que una vez hecho eso, meterse a reescribir el historial puede no merecer la pena. Un force push no alcanza las copias en forks y clones, ni las vistas en caché a las que se llega por el hash del commit. Dejar la clave inservible cubre todas las copias de una vez.