[{"data":1,"prerenderedAt":712},["ShallowReactive",2],{"blog-es-openai-api-key-exposed-in-frontend":3,"blog-index-es":470},{"id":4,"title":5,"body":6,"category":427,"cover":428,"coverAlt":429,"description":430,"draft":431,"extension":432,"faq":433,"image":449,"keywords":450,"meta":456,"navigation":457,"ogTitle":458,"path":459,"published":460,"seo":461,"stem":462,"tldr":463,"updated":468,"__hash__":469},"blog_es\u002Fblog\u002Fopenai-api-key-exposed-in-frontend.md","Tu clave de API de OpenAI está expuesta en tu frontend. Rótala.",{"type":7,"value":8,"toc":413},"minimark",[9,18,26,35,40,43,54,57,60,64,67,82,96,99,103,106,114,117,123,130,133,139,150,153,157,160,163,166,169,173,176,179,184,187,213,216,219,223,241,244,247,252,347,350,366,370,396],[10,11,12,13,17],"p",{},"Abre tu app en un navegador, mira el código fuente de la página y busca dentro\n",[14,15,16],"code",{},"sk-proj-",". Si aparece una cadena larga, tu clave de API de OpenAI está expuesta\nen tu frontend, y cualquier visitante que hayas tenido podría haberla copiado.",[10,19,20,21,25],{},"Aquí está la parte que casi todos los consejos sobre esto se equivocan: ",[22,23,24],"strong",{},"una\nclave de OpenAI no es una clave de Google con otro prefijo, y no hay nada que\npuedas configurar en un panel que la haga segura donde está."," Una clave de\nGoogle Maps va en tu página, y una opción gratuita la protege. Una clave de\nOpenAI no tiene ninguna opción equivalente en ninguna parte, y por eso la única\nprimera instrucción honesta es rotarla.",[10,27,28,29,34],{},"Entre el 12 y el 14 de agosto de 2026 pasamos nueve comprobaciones externas por\n30.998 apps en producción, construidas con Lovable, Bolt, v0, Replit y Base44.\nApareció una clave de OpenAI en 33 de ellas. Las 33 volvieron con nota D o F,\nporque un solo hallazgo crítico tapa la nota sin importar qué más hiciera bien la\napp. Las cifras completas están en\n",[30,31,33],"a",{"href":32},"\u002Fresearch\u002Fvibe-coded-app-security-2026","nuestro informe de escaneo",".",[36,37,39],"h2",{"id":38},"una-clave-de-api-de-openai-expuesta-en-tu-frontend-es-un-problema","¿Una clave de API de OpenAI expuesta en tu frontend es un problema?",[10,41,42],{},"Sí. Este es el caso en el que la alarma tiene razón.",[10,44,45,46,49,50,53],{},"Una clave de OpenAI es un ",[22,47,48],{},"token al portador",", y la palabra lleva dentro toda\nla explicación: quien la porta puede usarla. Viaja en una cabecera que dice\n",[14,51,52],{},"Authorization: Bearer sk-proj-…",", y los servidores de OpenAI no preguntan nada\nmás. Ni qué sitio web la envió. Ni de qué país vino, ni si quien la manda eres\ntú.",[10,55,56],{},"Piensa en un billete de tren y no en un pasaporte. Un revisor no comprueba de\nquién es el nombre que figura en un billete, porque tenerlo es toda la\nacreditación. Eso es lo que hace que un billete merezca la pena robarlo y un\npasaporte casi nunca.",[10,58,59],{},"Así que una clave impresa en tu app es un billete que le has dado a cada\nvisitante. La mayoría no mirará nunca. Los que miran no suelen ser personas:\nscrapers automáticos rastrean páginas públicas recogiendo cadenas con forma de\nclave, y no necesitan saber quién eres para encontrar la tuya.",[36,61,63],{"id":62},"por-qué-la-variable-de-entorno-no-la-escondió","Por qué la variable de entorno no la escondió",[10,65,66],{},"Porque una compilación de frontend mete las variables de entorno dentro del\narchivo que publica.",[10,68,69,70,73,74,77,78,81],{},"Este es el paso que hace que la gente se quede tranquila. Sacaste la clave de tu\ncódigo y la pusiste en un archivo ",[14,71,72],{},".env",", la llamaste ",[14,75,76],{},"VITE_OPENAI_API_KEY"," o\n",[14,79,80],{},"NEXT_PUBLIC_OPENAI_API_KEY",", y ahora el código nombra una variable donde antes\nestaba la clave. Nada cambió en la app que se publica. La herramienta de\ncompilación sustituyó esa variable por su valor al salir, y el valor está en el\nJavaScript que tu visitante descarga.",[10,83,84,85,88,89,91,92,95],{},"La documentación de Vite lo dice sin rodeos: las variables con el prefijo ",[14,86,87],{},"VITE_","\nquedan expuestas en el código fuente del cliente después del empaquetado, y la\ninformación sensible como las claves de API no debe ir en una de ellas, porque\nlos valores se empaquetan dentro de tu código. Los prefijos ",[14,90,87],{}," y\n",[14,93,94],{},"NEXT_PUBLIC_"," no son una caja segura. Son una declaración de que entiendes que\nesa variable es pública.",[10,97,98],{},"Una variable de entorno sí hizo una cosa real por ti: mantuvo la clave fuera de\ntu repositorio, donde se la habría encontrado cualquiera que leyera tu código.\nTus visitantes nunca leen tu código. Leen el archivo que tu compilación produjo a\npartir de él.",[36,100,102],{"id":101},"por-qué-no-puedes-restringirla-como-restringes-una-clave-de-google","Por qué no puedes restringirla como restringes una clave de Google",[10,104,105],{},"Porque OpenAI no ofrece una restricción de ese tipo.",[10,107,108,109,113],{},"Si has leído sobre una clave de API de Google en un frontend, te topaste con un\narreglo de cinco minutos: abre la clave en la consola de Google Cloud, pon una\nrestricción de referente HTTP, y la clave impresa en tu página funciona en tu\nsitio y devuelve un error en cualquier otro.\n",[30,110,112],{"href":111},"\u002Fblog\u002Fgoogle-api-key-exposed-in-frontend","Ese consejo es correcto para una clave de Google",",\ny no se traslada.",[10,115,116],{},"No hay ningún campo en una clave de OpenAI para \"solo desde yourapp.com\". Ni\nlista de dominios permitidos, ni comprobación de referente, ni restricción por IP\nque sobreviva a un navegador. Lo que OpenAI te da en su lugar está en la cuenta\nque hay detrás de la clave: a qué proyecto pertenece, cuánto puede gastar ese\nproyecto al mes y si la clave sigue existiendo. Eso limita lo que puede costar\nuna clave robada. La clave en sí sigue funcionando desde cualquier parte hasta\nque la borras.",[118,119],"diagram",{"alt":120,"caption":121,"src":122},"Dos carriles. En el carril superior una clave de Google sale de dos sitios web distintos y se topa con una barrera sólida: la petición de yourapp.com la atraviesa y llega a Google, y la de other.example se detiene y se marca con una cruz. En el carril inferior una clave de OpenAI sale de esos mismos dos sitios, no hay ninguna barrera dibujada, y ambas peticiones llegan a api.openai.com.","El carril superior es el arreglo del que le hablan a la gente. En el inferior no hay nada que dibujar, porque OpenAI no tiene ninguna opción de esa forma.","\u002Fblog\u002Fopenai-api-key-exposed-in-frontend\u002Fno-gate-to-set-1600x820.png",[36,124,126,127],{"id":125},"qué-hace-realmente-dangerouslyallowbrowser","Qué hace realmente ",[14,128,129],{},"dangerouslyAllowBrowser",[10,131,132],{},"Apaga una protección, y su nombre es la documentación.",[10,134,135,136,138],{},"La biblioteca oficial de JavaScript de OpenAI se niega a ejecutarse en un\nnavegador por defecto. El README dice que el soporte de navegador está\n\"disabled by default to avoid exposing your secret API credentials\", y que\nactivar ",[14,137,129],{}," \"can be dangerous because it exposes your\nsecret API credentials in the client-side code\".",[10,140,141,142,145,146,149],{},"Si tu app llama a OpenAI desde el navegador, esa opción está puesta a ",[14,143,144],{},"true"," en\nalgún sitio de tu código, porque la biblioteca no arranca sin ella. Alguien\nescribió la palabra ",[14,147,148],{},"dangerously"," para quitarse de encima un mensaje de error.\nAsí es como suele producirse este hallazgo, y la biblioteca te lo dijo primero.",[10,151,152],{},"La opción tiene usos reales, y todos son estrechos: una herramienta interna donde\nconoces a cada usuario, una clave de desarrollo temporal, una clave tan acotada\nque gastarla no cuesta nada. Una app pública en internet abierto no es ninguno de\nesos.",[36,154,156],{"id":155},"qué-cuesta-que-alguien-encuentre-tu-clave","Qué cuesta que alguien encuentre tu clave",[10,158,159],{},"Una factura, y una app que deja de funcionar.",[10,161,162],{},"La factura es la parte que la gente espera. Las peticiones de otra persona se\ncargan a tu cuenta, y las llamadas a un modelo no son baratas para lo que suele\nser un proyecto paralelo. Lo habitual es que quien lo lleva se dé cuenta por una\nfactura mayor que la del mes pasado sin ninguna razón que pueda señalar.",[10,164,165],{},"La caída es la parte que no espera. Tu cuenta tiene límites de tasa, y el tráfico\nde un desconocido se los gasta. Tu propia app empieza a devolver errores durante\nlas horas en las que otro está ocupado, y eso se lee como un fallo y no como un\nrobo, así que la gente pasa un día depurando lo que no es.",[10,167,168],{},"Hay un tercer coste, y depende de cómo estuviera acotada la clave. Una clave de\nAPI autentica todas las llamadas que puede hacer el proyecto al que pertenece,\nasí que una clave con permisos amplios alcanza todo lo demás que haya guardado\nahí: archivos subidos a la cuenta, modelos afinados, asistentes que construiste.\nComprueba qué podía alcanzar la clave antes de decidir que esto iba solo de\ndinero.",[36,170,172],{"id":171},"cómo-llamar-a-openai-desde-tu-app-sin-publicar-la-clave","Cómo llamar a OpenAI desde tu app sin publicar la clave",[10,174,175],{},"Pon algo tuyo entre tu visitante y OpenAI.",[10,177,178],{},"La forma es la misma en todas partes. Tu app llama a un pequeño endpoint que es\ntuyo. Ese endpoint guarda la clave, llama a OpenAI y devuelve la respuesta. El\nnavegador no ve nunca la clave, porque la clave nunca sale de tu servidor.",[118,180],{"alt":181,"caption":182,"src":183},"Dos filas. En la fila de arriba un panel de navegador contiene un valor de clave en rojo y una línea sale de ahí directamente hacia api.openai.com. En la fila de abajo el panel del navegador no contiene ninguna clave, una línea va hasta una caja de servidor que guarda la clave en verde azulado, y una segunda línea continúa del servidor a api.openai.com.","La misma petición en ambas filas. Lo que cambia es de qué lado de tu propio servidor está la clave.","\u002Fblog\u002Fopenai-api-key-exposed-in-frontend\u002Fwho-holds-the-key-1600x720.png",[10,185,186],{},"Dónde vive ese endpoint depende de con qué hayas construido:",[188,189,190,197,207],"ul",{},[191,192,193,196],"li",{},[22,194,195],{},"Supabase en tu stack",": una Edge Function, con la clave guardada como secreto en el panel de Supabase.",[191,198,199,202,203,206],{},[22,200,201],{},"Desplegado en Vercel o Netlify",": una función serverless bajo ",[14,204,205],{},"\u002Fapi",", con la clave en las variables de entorno de servidor del proyecto.",[191,208,209,212],{},[22,210,211],{},"Lovable, Bolt o Replit",": cada uno tiene su propio almacén de secretos. La regla no cambia, y la trampa tampoco: un almacén etiquetado como secretos sigue enviando el valor al navegador si el código que lo lee se ejecuta ahí.",[10,214,215],{},"Dos cosas que merece la pena hacer ya que estás. Pon un límite de tasa a tu\nendpoint, porque un endpoint que llama a OpenAI para cualquiera que pregunte es\nla misma factura por una vía más lenta. Y pon un límite de gasto mensual al\nproyecto de OpenAI, que es el único control que le pone suelo al peor caso.",[10,217,218],{},"Si tu app usa la API Realtime de OpenAI para voz, el navegador sí necesita una\ncredencial de verdad, y OpenAI documenta cómo dársela: tu servidor acuña un\nsecreto de cliente de vida corta y le entrega ese a la página. El billete sigue\nexistiendo, caduca en minutos, y lo emitió tu propio servidor.",[36,220,222],{"id":221},"cómo-encontrar-gratis-cada-clave-que-publica-tu-app","Cómo encontrar gratis cada clave que publica tu app",[10,224,225,226,228,229,232,233,236,237,240],{},"Empieza a mano, porque cuesta cinco minutos y no requiere instalar nada. Mira el\ncódigo fuente de tu sitio en producción y busca dentro ",[14,227,16],{}," para una clave\nde OpenAI, ",[14,230,231],{},"sk-ant-"," para una de Anthropic, ",[14,234,235],{},"AIza"," para Google y ",[14,238,239],{},"eyJ"," para un\ntoken de Supabase.",[10,242,243],{},"Lo que se escapa así es el JavaScript que la página carga después, que en una app\nhecha a base de vibe coding es casi todo. Nuestro escáner abre tu app en un\nnavegador real, espera a que lleguen los bundles y lee esos en lugar del HTML.\nAdemás descodifica cada token de Supabase e informa del rol escrito dentro, así\nque una clave que sí va en tu página vuelve marcada como correcta y no enterrada\nen un muro de rojo.",[10,245,246],{},"La comprobación de claves es una de nueve, y las otras ocho son la razón por la\nque una nota te dice más que una búsqueda:",[118,248],{"alt":249,"caption":250,"src":251},"La marca de Reeve sobre una fila de nueve iconos de trazo, uno por comprobación: una llave, un cilindro de base de datos, un documento, un candado, una caja con tapa, un par de corchetes de código, un globo terráqueo, una escarapela de certificado y un calendario.","Las nueve comprobaciones de solo lectura que hace un escaneo, en el orden de la lista de debajo. Todas se leen desde fuera, tal como ve tu app un desconocido.","\u002Fblog\u002Fopenai-api-key-exposed-in-frontend\u002Fnine-checks-1600x420.png",[253,254,255,268],"table",{},[256,257,258],"thead",{},[259,260,261,265],"tr",{},[262,263,264],"th",{},"Qué mira el escaneo",[262,266,267],{},"La pregunta que responde",[269,270,271,280,288,299,307,315,323,331,339],"tbody",{},[259,272,273,277],{},[274,275,276],"td",{},"Claves secretas en tu código",[274,278,279],{},"¿Hay una clave de API de pago o de admin legible por cualquiera?",[259,281,282,285],{},[274,283,284],{},"Reglas de la base de datos",[274,286,287],{},"¿Puede un desconocido leer las filas de tus usuarios sin entrar?",[259,289,290,293],{},[274,291,292],{},"Archivos privados",[274,294,295,296,298],{},"¿Se pueden descargar archivos ",[14,297,72],{}," o volcados de base de datos?",[259,300,301,304],{},[274,302,303],{},"Cabeceras de seguridad",[274,305,306],{},"¿Están activadas las protecciones del lado del navegador?",[259,308,309,312],{},[274,310,311],{},"Buckets de almacenamiento",[274,313,314],{},"¿Puede cualquiera listar los archivos que suben tus usuarios?",[259,316,317,320],{},[274,318,319],{},"Source maps",[274,321,322],{},"¿Está tu código fuente original publicado junto a la app?",[259,324,325,328],{},[274,326,327],{},"APIs abiertas y CORS",[274,329,330],{},"¿Responden tus endpoints a cualquier sitio web que pregunte?",[259,332,333,336],{},[274,334,335],{},"Caducidad del certificado",[274,337,338],{},"¿Es válido el HTTPS y no está a punto de caducar?",[259,340,341,344],{},[274,342,343],{},"Renovación del dominio",[274,345,346],{},"¿Está el nombre renovado antes de que otro pueda quedárselo?",[10,348,349],{},"Obtienes una nota, una puntuación y los recuentos en pantalla en unos 20\nsegundos, sin cuenta. Da una dirección de correo y viene con ella la lista\ndetallada, junto con un arreglo escrito para tu builder que puedes pegar\ndirectamente.",[10,351,352,353,356,357,361,362,34],{},"Tres cosas que no hará, y son las razones por las que es seguro apuntarlo a una\napp en producción: nunca inicia sesión, nunca escribe nada y nunca se queda con\nuna clave que encuentra. Un secreto expuesto se guarda como una pista enmascarada\nde la forma ",[14,354,355],{},"sk-proj-…a1b2",", y el valor real se descarta.\n",[30,358,360],{"href":359},"\u002F#scan","Escanea tu app",", o lee antes\n",[30,363,365],{"href":364},"\u002Fsecurity-scanner","qué mira cada una de las nueve comprobaciones",[36,367,369],{"id":368},"qué-hacer-ahora-mismo","Qué hacer ahora mismo",[371,372,373],"key-takeaways",{},[188,374,375,378,381,384,387],{},[191,376,377],{},"Rota la clave primero, en el panel de OpenAI bajo API keys. Borrarla de tu código no cierra nada, porque el valor antiguo sigue en tu historial de versiones y en cada copia en caché de tu página.",[191,379,380],{},"Pon un límite de gasto mensual al proyecto al que pertenece la clave. Es el único control que pone un techo a lo que este error o cualquier otro posterior pueden costarte.",[191,382,383],{},"Mueve la llamada detrás de un endpoint tuyo, y dale a ese endpoint su propio límite de tasa. Un navegador no debería tener nunca una clave que gasta dinero.",[191,385,386],{},"Lee tu página de uso para los días en que la clave estuvo activa. Rotar detiene lo que pasa a partir de ahora y no dice nada de lo que ya pasó.",[191,388,389,390,392,393,395],{},"Deja el prefijo ",[14,391,87],{}," o ",[14,394,94],{}," fuera de todo lo que te molestaría que leyera un desconocido. Esos prefijos significan público, y tu herramienta de compilación se los toma al pie de la letra.",[10,397,398,399,403,404,408,409,34],{},"Si prefieres recorrer esto de una sentada, la\n",[30,400,402],{"href":401},"\u002Fchecklist","lista de seguridad de 10 minutos"," lo cubre junto con las otras cosas\nque conviene cerrar en una app recién lanzada. Y para la pregunta más amplia de\nqué claves van en un navegador, tenemos\n",[30,405,407],{"href":406},"\u002Fblog\u002Fwhich-api-keys-are-safe-in-your-frontend","una guía para distinguir las publicables de las secretas","\ny\n",[30,410,412],{"href":411},"\u002Fblog\u002Fwhat-secrets-leak-from-vibe-coded-apps","un censo de lo que 30.998 apps publicaron de verdad",{"title":414,"searchDepth":415,"depth":415,"links":416},"",3,[417,419,420,421,423,424,425,426],{"id":38,"depth":418,"text":39},2,{"id":62,"depth":418,"text":63},{"id":101,"depth":418,"text":102},{"id":125,"depth":418,"text":422},"Qué hace realmente dangerouslyAllowBrowser",{"id":155,"depth":418,"text":156},{"id":171,"depth":418,"text":172},{"id":221,"depth":418,"text":222},{"id":368,"depth":418,"text":369},"Fundamentos de seguridad","\u002Fblog\u002Fopenai-api-key-exposed-in-frontend\u002Fcover-1200x630.png","Un valor con forma de clave dentro de una página de código de app, dibujado tan largo que se sale por el borde derecho del panel que lo contiene.","Una clave de API de OpenAI expuesta en tu frontend no se puede atar a un dominio. Rótala hoy, mueve la llamada a tu servidor y limita el gasto.",false,"md",[434,437,440,443,446],{"q":435,"a":436},"Alguien encontró mi clave de OpenAI en mi app. ¿Qué hago primero?","Rotarla. Abre tu panel de OpenAI en API keys, crea una clave nueva, pon la nueva en tu servidor y borra la vieja. Borrarla de tu código no es lo mismo, porque el valor antiguo sigue en tu historial de versiones y en cualquier copia en caché de tu página. Después pon un límite de gasto al proyecto y lee tu página de uso para los días en que la clave estuvo activa.",{"q":438,"a":439},"¿Puedo restringir una clave de OpenAI a mi propio dominio, como hago con una de Google?","No. Una clave de API de Google admite una restricción de referente HTTP que la hace funcionar en tu sitio y fallar en todos los demás, y por eso una clave de Google en tu página suele ser inofensiva. OpenAI no ofrece nada equivalente. No hay lista de dominios permitidos ni comprobación de referente para una clave de API, así que los únicos controles que tienes están sobre la cuenta que hay detrás: a qué proyecto pertenece la clave, cuánto puede gastar ese proyecto y si la clave sigue existiendo.",{"q":441,"a":442},"La biblioteca de OpenAI tiene una opción dangerouslyAllowBrowser. ¿Eso la hace segura?","No, y el nombre es el aviso. OpenAI entrega el soporte de navegador desactivado, y su propio README dice que la opción es peligrosa porque expone tus credenciales secretas de API en el código del cliente. Activarla no cambia lo que el navegador puede leer; solo evita que la biblioteca se niegue a arrancar. Los casos estrechos para los que está pensada son herramientas internas con usuarios conocidos y claves de desarrollo de vida corta, no una app pública.",{"q":444,"a":445},"¿Cuánto puede gastar alguien con una clave que ha encontrado?","Todo lo que permita tu cuenta, y por eso el límite de gasto importa más que el tamaño de la factura que hayas visto hasta ahora. Una clave robada consume los mismos límites de tasa que usa tu app, así que el primer síntoma a menudo no es la factura: es tu propia app fallando mientras otra persona está ocupada. Pon un límite de gasto mensual al proyecto y tendrás un techo para todo esto.",{"q":447,"a":448},"Solo usé la clave para una demo rápida. ¿Importa igualmente?","Sí. Una clave sigue activa hasta que alguien la revoca, y no sabe que se suponía que era temporal. Los scrapers automáticos recogen cadenas con forma de clave de páginas públicas de manera continua, así que la antigüedad de la demo juega en tu contra y no a tu favor. Borrar la clave lleva menos tiempo que decidir si merecía la pena borrarla.","\u002Fblog\u002Fopenai-api-key-exposed-in-frontend\u002Fcard-800x500.png",[451,452,453,454,129,455],"clave api de openai expuesta frontend","ocultar clave api de openai en react","llamar a openai desde el frontend de forma segura","seguridad de la clave api de openai","clave api de openai filtrada",{},true,"¿Clave de API de OpenAI expuesta en tu frontend?","\u002Fblog\u002Fopenai-api-key-exposed-in-frontend","2026-09-03",{"title":5,"description":430},"blog\u002Fopenai-api-key-exposed-in-frontend",[464,465,466,467],"Una clave de API de OpenAI expuesta en tu frontend es el caso en el que la alarma tiene razón. Ninguna opción de configuración hace que una clave así sea segura en un navegador.","Es un token al portador, así que basta con tenerla. No existe ninguna restricción de dominio que la ate a tu propio sitio, como sí la hay para una clave de Google.","Rótala hoy en el panel de OpenAI, mueve la llamada detrás de un endpoint tuyo y pon un límite de gasto al proyecto.","Encontramos una en 33 de 30.998 apps escaneadas. Rara, y las 33 salieron con nota D o F.","2026-09-21","m_xtw2ZAKOH5wFTvXDUK1PxbDbY5tOnOYTsLLQVqXL8",[471,478,484,489,495,501,507,513,519,525,531,537,543,549,555,561,567,573,579,585,591,592,597,603,609,615,621,627,633,639,645,651,657,662,668,674,679,685,691,697,702,708],{"path":472,"title":473,"description":474,"published":475,"category":476,"image":477,"draft":431},"\u002Fblog\u002Fsupabase-backup-auth-users","Por qué tu dump de Supabase no tiene usuarios dentro","Ejecuta supabase db dump por sí solo y obtienes la forma de tu base de datos y ninguna de sus filas, sin el esquema auth donde viven tus usuarios.","2026-09-23","Backups","\u002Fblog\u002Fsupabase-backup-auth-users\u002Fcard-800x500.png",{"path":479,"title":480,"description":481,"published":482,"category":427,"image":483,"draft":431},"\u002Fblog\u002Fdomain-and-certificate-expiry","Dominio caducado, web caída: qué pasa a partir de ahora","Tu dominio caducó y tu web está caída. Aquí tienes el reloj que acaba de arrancar, por qué el certificado es el menor problema, y cómo comprobar ambos.","2026-09-22","\u002Fblog\u002Fdomain-and-certificate-expiry\u002Fcard-800x500.png",{"path":485,"title":486,"description":487,"published":468,"category":427,"image":488,"draft":431},"\u002Fblog\u002Fis-lovable-safe","¿Es seguro Lovable? Lo que vimos en 18.554 aplicaciones Lovable","¿Es seguro Lovable? Pasamos 18.554 aplicaciones Lovable por nueve comprobaciones. La plataforma fue la más limpia de cinco. Los hallazgos estaban dentro.","\u002Fblog\u002Fis-lovable-safe\u002Fcard-800x500.png",{"path":490,"title":491,"description":492,"published":493,"category":427,"image":494,"draft":431},"\u002Fblog\u002Fvibe-coded-app-security-checklist","La lista de seguridad del vibe coding, en nueve comprobaciones","Una lista de seguridad del vibe coding con nueve puntos, cada uno verificable en tu app desde fuera por cualquiera, y cada uno con una prueba de una línea.","2026-09-20","\u002Fblog\u002Fvibe-coded-app-security-checklist\u002Fcard-800x500.png",{"path":496,"title":497,"description":498,"published":499,"category":427,"image":500,"draft":431},"\u002Fblog\u002Fstripe-secret-key-in-frontend","Una clave secreta de Stripe en tu frontend puede mover dinero","Una clave secreta de Stripe expuesta en tu frontend puede reembolsar, cobrar y leer cada ficha de cliente que tienes. Tu clave pk_live_ sí debe estar ahí.","2026-09-19","\u002Fblog\u002Fstripe-secret-key-in-frontend\u002Fcard-800x500.png",{"path":502,"title":503,"description":504,"published":505,"category":427,"image":506,"draft":431},"\u002Fblog\u002Fvite-and-next-public-env-vars","Variables de entorno de Vite expuestas: el prefijo dice publica esto","Variables de entorno de Vite expuestas en tu app: hicieron lo que pedía el prefijo. VITE_ y NEXT_PUBLIC_ significan publica esto, y la IA nunca supo el coste.","2026-09-18","\u002Fblog\u002Fvite-and-next-public-env-vars\u002Fcard-800x500.png",{"path":508,"title":509,"description":510,"published":511,"category":427,"image":512,"draft":431},"\u002Fblog\u002Fbase44-source-maps","Seguridad en Base44: qué marca un escaneo y qué te toca arreglar a ti","Seguridad en Base44 en 5.438 apps escaneadas: tres hallazgos en casi todas son de la plataforma, el source map es la insignia de Base44 y lo tuyo es poco.","2026-09-17","\u002Fblog\u002Fbase44-source-maps\u002Fcard-800x500.png",{"path":514,"title":515,"description":516,"published":517,"category":427,"image":518,"draft":431},"\u002Fblog\u002Fis-cursor-ai-safe","¿Es seguro Cursor AI? El editor, el código y la app que publicaste","¿Es seguro Cursor AI? Tres preguntas en una búsqueda: qué guarda Cursor, en qué falla el código que escribe y si la app que publicaste está abierta.","2026-09-16","\u002Fblog\u002Fis-cursor-ai-safe\u002Fcard-800x500.png",{"path":520,"title":521,"description":522,"published":523,"category":427,"image":524,"draft":431},"\u002Fblog\u002Fis-replit-safe","¿Es seguro Replit? Lo que vimos en 3.042 aplicaciones Replit en vivo","¿Es seguro Replit? Pasamos 3.042 aplicaciones Replit en vivo por nueve comprobaciones externas. Los hallazgos estaban en la aplicación publicada, no en el host.","2026-09-15","\u002Fblog\u002Fis-replit-safe\u002Fcard-800x500.png",{"path":526,"title":527,"description":528,"published":529,"category":476,"image":530,"draft":431},"\u002Fblog\u002Fsupabase-storage-backup","Supabase storage backup: tu copia de la base no tiene archivos","Un Supabase storage backup es una tarea aparte. Las copias de la base guardan la lista de tus archivos y ninguno de ellos, así que cada upload queda roto.","2026-09-14","\u002Fblog\u002Fsupabase-storage-backup\u002Fcard-800x500.png",{"path":532,"title":533,"description":534,"published":535,"category":476,"image":536,"draft":431},"\u002Fblog\u002Fsupabase-point-in-time-recovery","Point-in-time recovery de Supabase: precio y límites","El point-in-time recovery de Supabase rebobina tu base de datos a cualquier segundo de la última semana. Cuesta $100 al mes sobre Pro y solo cubre la base.","2026-09-13","\u002Fblog\u002Fsupabase-point-in-time-recovery\u002Fcard-800x500.png",{"path":538,"title":539,"description":540,"published":541,"category":476,"image":542,"draft":431},"\u002Fblog\u002Fsupabase-project-paused-recover","¿Proyecto de Supabase pausado? Tus datos siguen ahí","Supabase pausó tu proyecto tras una semana sin actividad. No se ha borrado nada, Restaurar está junto al nombre y tienes un año antes de que eso cambie.","2026-09-12","\u002Fblog\u002Fsupabase-project-paused-recover\u002Fcard-800x500.png",{"path":544,"title":545,"description":546,"published":547,"category":427,"image":548,"draft":431},"\u002Fblog\u002Fsafest-ai-app-builder","¿Qué builder de apps con IA es más seguro? 30.998 apps escaneadas","¿Qué builder de apps con IA es más seguro? Escaneamos 30.998 apps activas de Lovable, Base44, Replit, v0 y Bolt. El builder no es lo que decide tu nota.","2026-09-11","\u002Fblog\u002Fsafest-ai-app-builder\u002Fcard-800x500.png",{"path":550,"title":551,"description":552,"published":553,"category":427,"image":554,"draft":431},"\u002Fblog\u002Fenable-rls-on-every-supabase-table","Activa Row Level Security en cada tabla de Supabase y compruébalo","Activar Row Level Security en Supabase sin política cierra la tabla del todo. Una política sin el interruptor no hace nada. Aquí tienes el SQL y la prueba.","2026-09-10","\u002Fblog\u002Fenable-rls-on-every-supabase-table\u002Fcard-800x500.png",{"path":556,"title":557,"description":558,"published":559,"category":427,"image":560,"draft":431},"\u002Fblog\u002Fsupabase-rls-disabled-in-public","Supabase \"RLS disabled in public\": lo que el aviso no ve","Supabase marca \"RLS disabled in public\" como error. No dice nada de la política de lectura que deja tu tabla igual de abierta a cualquiera.","2026-09-09","\u002Fblog\u002Fsupabase-rls-disabled-in-public\u002Fcard-800x500.png",{"path":562,"title":563,"description":564,"published":565,"category":427,"image":566,"draft":431},"\u002Fblog\u002Frotate-supabase-service-role-key","Cómo rotar una clave service_role de Supabase filtrada","Supabase dice que primero cierres la fuga. Otras guías dicen que rotes ya. Cuál es lo correcto depende de dónde se filtró tu clave service_role.","2026-09-08","\u002Fblog\u002Frotate-supabase-service-role-key\u002Fcard-800x500.png",{"path":568,"title":569,"description":570,"published":571,"category":427,"image":572,"draft":431},"\u002Fblog\u002Fvibe-coding-security-scanners-compared","Escáneres de seguridad vibe coding comparados, el nuestro incluido","El mejor escáner de seguridad para vibe coding depende de tres preguntas que ninguna lista de funciones responde. Diez herramientas, con precios.","2026-09-07","\u002Fblog\u002Fvibe-coding-security-scanners-compared\u002Fcard-800x500.png",{"path":574,"title":575,"description":576,"published":577,"category":427,"image":578,"draft":431},"\u002Fblog\u002Fsupabase-security-checker","Supabase security checker: haz tú mismo las cinco comprobaciones","Un Supabase security checker lee tu app publicada en lugar de la configuración de tu proyecto. Estas son las cinco comprobaciones y cómo hacer cada una.","2026-09-06","\u002Fblog\u002Fsupabase-security-checker\u002Fcard-800x500.png",{"path":580,"title":581,"description":582,"published":583,"category":427,"image":584,"draft":431},"\u002Fblog\u002Fvibe-coding-security-scanner","Escáner de seguridad para vibe coding: qué se le escapa","Un escáner de seguridad para vibe coding lee tu app en vivo desde fuera. Qué cubre eso, las cuatro cosas que no puede ver y cómo leer el resultado.","2026-09-05","\u002Fblog\u002Fvibe-coding-security-scanner\u002Fcard-800x500.png",{"path":586,"title":587,"description":588,"published":589,"category":427,"image":590,"draft":431},"\u002Fblog\u002Fmissing-security-headers","Faltan cabeceras de seguridad: cuándo importa de verdad","Que falten cabeceras de seguridad es el hallazgo que más imprime nuestro escáner. Contra qué protegen y cuándo son la línea menos urgente de tu informe.","2026-09-04","\u002Fblog\u002Fmissing-security-headers\u002Fcard-800x500.png",{"path":459,"title":5,"description":430,"published":460,"category":427,"image":449,"draft":431},{"path":411,"title":593,"description":594,"published":595,"category":427,"image":596,"draft":431},"Clave de API expuesta en el frontend: qué llevan 30.998 apps","Una clave de API expuesta en tu frontend suele ser una clave de Google Maps. Escaneamos 30.998 apps vibe-coded y contamos qué secretos se filtran de verdad.","2026-09-02","\u002Fblog\u002Fwhat-secrets-leak-from-vibe-coded-apps\u002Fcard-800x500.png",{"path":598,"title":599,"description":600,"published":601,"category":476,"image":602,"draft":431},"\u002Fblog\u002Fsupabase-backup-tools-compared","Herramientas de backup de Supabase comparadas, la nuestra incluida","Cuatro tipos de herramienta de backup de Supabase, qué copia cada una y cuándo una GitHub Action gratuita le gana a pagar a cualquiera, nosotros incluidos.","2026-09-01","\u002Fblog\u002Fsupabase-backup-tools-compared\u002Fcard-800x500.png",{"path":604,"title":605,"description":606,"published":607,"category":427,"image":608,"draft":431},"\u002Fblog\u002Freplit-secrets-explained","Cómo usar secrets en Replit, y qué se publica de todos modos","Cómo usar secrets en Replit: añadir uno, leerlo y arreglar las dos causas del undefined. Y las claves que la herramienta Secrets no puede mantener privadas.","2026-08-31","\u002Fblog\u002Freplit-secrets-explained\u002Fcard-800x500.png",{"path":610,"title":611,"description":612,"published":613,"category":476,"image":614,"draft":431},"\u002Fblog\u002Fback-up-supabase-free-tier","Backups del plan gratuito de Supabase: cómo hacer uno sin terminal","No hay backups en el plan gratuito de Supabase: la copia tienes que hacerla tú. Cómo sacarla desde el panel y qué deja fuera la vía CSV.","2026-08-30","\u002Fblog\u002Fback-up-supabase-free-tier\u002Fcard-800x500.png",{"path":616,"title":617,"description":618,"published":619,"category":427,"image":620,"draft":431},"\u002Fblog\u002Fis-supabase-secure","¿Supabase es seguro? Sí. Tu proyecto es otra pregunta","¿Supabase es seguro? La plataforma está auditada, cifrada y sometida a pentests. Su propia documentación dice dónde termina eso y empiezan tus ajustes.","2026-08-29","\u002Fblog\u002Fis-supabase-secure\u002Fcard-800x500.png",{"path":622,"title":623,"description":624,"published":625,"category":427,"image":626,"draft":431},"\u002Fblog\u002Fwhere-to-find-supabase-api-keys","Dónde están tus claves API de Supabase: anon, service_role y URL","La URL del proyecto, la clave anon y la clave service_role están en una sola página del panel. Aquí tienes dónde está, y cuál de los cuatro va en tu app.","2026-08-28","\u002Fblog\u002Fwhere-to-find-supabase-api-keys\u002Fcard-800x500.png",{"path":628,"title":629,"description":630,"published":631,"category":427,"image":632,"draft":431},"\u002Fblog\u002Fnew-row-violates-row-level-security-policy","New row violates row-level security policy en Supabase. ¿Y ahora?","\"New row violates row-level security policy\" significa que Supabase rechazó una escritura. El arreglo rápido vuelve a abrir la tabla para cualquiera.","2026-08-27","\u002Fblog\u002Fnew-row-violates-row-level-security-policy\u002Fcard-800x500.png",{"path":634,"title":635,"description":636,"published":637,"category":427,"image":638,"draft":431},"\u002Fblog\u002Fcors-wildcard-security-risk","¿Es un comodín CORS un riesgo de seguridad? Casi nunca.","¿Es un comodín CORS un riesgo de seguridad? Casi siempre es el ajuste por defecto de tu builder y no entrega nada que tu servidor no diera ya a cualquiera.","2026-08-26","\u002Fblog\u002Fcors-wildcard-security-risk\u002Fcard-800x500.png",{"path":640,"title":641,"description":642,"published":643,"category":476,"image":644,"draft":431},"\u002Fblog\u002Fsupabase-branching-is-not-a-backup","Supabase branching no es un backup. Solo va hacia adelante.","Por qué el branching de Supabase no es un backup: una rama arranca sin tus datos y un merge solo mueve el esquema. Para qué sirve y qué usar en su lugar.","2026-08-25","\u002Fblog\u002Fsupabase-branching-is-not-a-backup\u002Fcard-800x500.png",{"path":646,"title":647,"description":648,"published":649,"category":476,"image":650,"draft":431},"\u002Fblog\u002Fhow-to-restore-a-supabase-backup","Cómo restaurar un backup de Supabase, y qué se rompe después","Cómo restaurar un backup de Supabase desde el panel o desde un archivo de volcado, qué reemplaza la restauración y por qué tu app puede seguir rota al terminar.","2026-08-24","\u002Fblog\u002Fhow-to-restore-a-supabase-backup\u002Fcard-800x500.png",{"path":652,"title":653,"description":654,"published":655,"category":427,"image":656,"draft":431},"\u002Fblog\u002Fsupabase-storage-bucket-public","Tu bucket de Supabase Storage es público. ¿Es un problema?","Un bucket público de Supabase Storage significa que quien tenga la URL de un archivo puede abrirlo. No significa que alguien pueda listar lo que hay dentro.","2026-08-23","\u002Fblog\u002Fsupabase-storage-bucket-public\u002Fcard-800x500.png",{"path":111,"title":658,"description":659,"published":660,"category":427,"image":661,"draft":431},"¿Es un problema una clave de API de Google en tu frontend?","Una clave de API de Google en tu frontend es la clave que más encuentra nuestro escáner, y casi siempre no pasa nada. Un ajuste gratuito decide cuál es cuál.","2026-08-22","\u002Fblog\u002Fgoogle-api-key-exposed-in-frontend\u002Fcard-800x500.png",{"path":663,"title":664,"description":665,"published":666,"category":476,"image":667,"draft":431},"\u002Fblog\u002Fai-agent-deleted-my-database","Un agente de IA borró mis datos de Supabase. ¿Qué puedo recuperar?","Un agente de IA borró datos de tu base. Lo que puedes recuperar ya estaba decidido antes de que se ejecutara, y los próximos minutos deciden el resto.","2026-08-21","\u002Fblog\u002Fai-agent-deleted-my-database\u002Fcard-800x500.png",{"path":669,"title":670,"description":671,"published":672,"category":427,"image":673,"draft":431},"\u002Fblog\u002Fcan-anyone-read-your-supabase-database","¿Puede cualquiera leer tu base de datos Supabase? 3.680 apps","¿Puede cualquiera leer tu base de datos Supabase sin iniciar sesión? Escaneamos 30.998 apps activas hechas con builders de IA y medimos cuántas dicen que sí.","2026-08-18","\u002Fblog\u002Fcan-anyone-read-your-supabase-database\u002Fcard-800x500.png",{"path":675,"title":676,"description":677,"published":672,"category":427,"image":678,"draft":431},"\u002Fblog\u002Fsource-maps-exposed-in-production","Source maps expuestos: tu app está publicando su código original","Un source map expuesto deja que cualquiera lea el código original de tu app, comentarios incluidos. La comprobación de 30 segundos y lo que de verdad importa.","\u002Fblog\u002Fsource-maps-exposed-in-production\u002Fcard-800x500.png",{"path":680,"title":681,"description":682,"published":683,"category":427,"image":684,"draft":431},"\u002Fblog\u002Fsupabase-new-api-keys","Las nuevas claves de API de Supabase: ¿cuál va en tu app?","Supabase sustituyó anon y service_role por claves publicables y secretas. ¿Cuál pertenece a tu app y cuál no debe estar nunca?","2026-08-12","\u002Fblog\u002Fsupabase-new-api-keys\u002Fcard-800x500.png",{"path":686,"title":687,"description":688,"published":689,"category":476,"image":690,"draft":431},"\u002Fblog\u002Fdoes-supabase-back-up-my-database","¿Supabase respalda mi base de datos? Depende de tu plan.","¿Supabase respalda tu base de datos? A diario en los planes de pago, y nada en el gratuito. Cómo saber cuál tienes y qué es lo que esa copia no cubre.","2026-08-11","\u002Fblog\u002Fdoes-supabase-back-up-my-database\u002Fcard-800x500.png",{"path":692,"title":693,"description":694,"published":695,"category":427,"image":696,"draft":431},"\u002Fblog\u002Fsupabase-rls-on-but-table-still-public","Supabase Row Level Security está activado. Tu tabla sigue pública.","Activar Supabase Row Level Security no protege una tabla. Lo hacen tus políticas, y la que arregló tu app rota puede dejar entrar a cualquiera.","2026-08-10","\u002Fblog\u002Fsupabase-rls-on-but-table-still-public\u002Fcard-800x500.png",{"path":698,"title":699,"description":700,"published":695,"category":476,"image":701,"draft":431},"\u002Fblog\u002Fversion-history-is-not-a-backup","El historial de versiones no es un backup. No recupera una tabla.","Lovable y Bolt guardan un historial de versiones de tu código. Tu base de datos es un servicio aparte, y volver atrás no te devuelve tus datos.","\u002Fblog\u002Fversion-history-is-not-a-backup\u002Fcard-800x500.png",{"path":703,"title":704,"description":705,"published":706,"category":476,"image":707,"draft":431},"\u002Fblog\u002Fthree-ways-to-back-up-a-supabase-database","Tres formas de respaldar una base Supabase, y qué falta en cada una","El panel, pg_dump y un servicio gestionado. Qué guarda de verdad cada uno, qué deja fuera sin avisar y cuál sobrevive a perder la cuenta.","2026-08-09","\u002Fblog\u002Fthree-ways-to-back-up-a-supabase-database\u002Fcard-800x500.png",{"path":406,"title":709,"description":710,"published":706,"category":427,"image":711,"draft":431},"Qué claves de API son seguras en el navegador y cuáles no","Tu clave anon de Supabase está pensada para ser pública. Tu clave service_role no, y se salta todas las reglas que pongas. Cómo distinguirlas.","\u002Fblog\u002Fwhich-api-keys-are-safe-in-your-frontend\u002Fcard-800x500.png",1790150954112]