[{"data":1,"prerenderedAt":921},["ShallowReactive",2],{"blog-es-open-api-endpoint-exposed":3,"blog-index-es":571},{"id":4,"title":5,"body":6,"category":529,"cover":530,"coverAlt":531,"description":532,"draft":533,"extension":534,"faq":535,"image":550,"keywords":551,"meta":559,"navigation":560,"ogTitle":561,"path":562,"published":563,"seo":564,"stem":565,"tldr":566,"updated":563,"__hash__":570},"blog_es\u002Fblog\u002Fopen-api-endpoint-exposed.md","¿Un endpoint de API abierto es un problema? Mira el JSON",{"type":7,"value":8,"toc":515},"minimark",[9,13,21,26,29,32,35,41,44,48,51,54,118,121,125,128,138,144,150,168,179,188,192,195,202,274,282,294,298,301,304,307,312,322,326,329,332,356,359,363,366,369,379,382,388,399,421,427,430,436,445,450,453,459,462,471,475,495,502],[10,11,12],"p",{},"Tu escaneo volvió con una línea que no suena a veredicto: algunos endpoints de\nAPI responden sin login. Puede que alguien te lo haya dicho de forma más\ndirecta y te haya hablado de un endpoint de API abierto. Debajo hay una\ndirección, y hay bastantes posibilidades de que no la reconozcas.",[10,14,15,16,20],{},"Aquí está la parte que guía tras guía cuenta mal: ",[17,18,19],"strong",{},"el consejo habitual para\neste hallazgo es poner un login delante de tu endpoint, y en la mayoría de las\naplicaciones que hemos escaneado no hay ningún endpoint tuyo delante del que\nponerlo."," Siete de cada diez de los que encontramos pertenecen a la plataforma\nen la que se publicó la aplicación. Así que leer este hallazgo empieza por\naveriguar de quién es la dirección, y la respuesta suele verse ya en la primera\nlínea de lo que vuelve.",[22,23,25],"h2",{"id":24},"qué-significa-un-endpoint-de-api-abierto-en-tu-informe","Qué significa un endpoint de API abierto en tu informe",[10,27,28],{},"Significa que una ruta escrita en el código de tu aplicación respondió a una\npetición que no llevaba login, y que lo que volvió fue JSON.",[10,30,31],{},"Tres pasos, y ninguno tiene truco. Cargamos tu aplicación en un navegador igual\nque lo hace un visitante, y leemos el código que ese navegador descargó. Dentro\nestán las direcciones a las que tu aplicación llama cuando necesita datos.\nEntonces le pedimos datos a cada una, sin cuenta, sin cookie y sin clave. Si una\ndirección responde con JSON que no es un mensaje de error, va al informe.",[10,33,34],{},"Eso recoge lo que pasó. No es un juicio sobre los datos, porque nada que esté\nfuera de tu aplicación puede saber si una lista de cosas es una lista de cosas\npúblicas. Por eso el hallazgo está redactado como está: estos endpoints\ndevolvieron datos sin ninguna autenticación, puede que sea intencionado para\ndatos públicos, comprueba que ninguno expone información privada.",[36,37],"diagram",{"alt":38,"caption":39,"src":40},"Código descargado de una aplicación, con cuatro direcciones extraídas de él. A cada dirección le pide datos una petición que lleva una llave tachada, es decir, sin login. Tres vuelven vacías o rechazadas y una vuelve con un bloque de datos.","Las direcciones salen de tu propio código, y a cada una se le pregunta sin nada adjunto. Lo que responda es lo que acaba en el informe.","\u002Fblog\u002Fopen-api-endpoint-exposed\u002Fwhat-the-check-does-1600x620.png",[10,42,43],{},"Así que el informe te entrega una lista de direcciones para abrir. Todas y cada\nuna hay que abrirlas en un navegador desconectado, y las primeras líneas de la\nrespuesta son las que zanjan el asunto.",[22,45,47],{"id":46},"todo-endpoint-de-api-abierto-es-un-problema-de-seguridad","¿Todo endpoint de API abierto es un problema de seguridad?",[10,49,50],{},"No. Hay tres tipos, y solo el tercero es un problema.",[10,52,53],{},"Piensa en un bloque de pisos. Algunas cosas están pensadas para cualquiera que\nentre: el horario junto a la puerta, las salidas de emergencia, el aviso de que\nel martes revisan el ascensor. En algún sitio de ese mismo edificio hay un\narchivador con los datos de los vecinos. Desde el pasillo, un tablón de\nanuncios y un archivador sin llave son igual de abribles, y la única manera de\ndistinguirlos es leer lo que pone el papel.",[55,56,57,73],"table",{},[58,59,60],"thead",{},[61,62,63,67,70],"tr",{},[64,65,66],"th",{},"Qué respondió",[64,68,69],{},"De quién es la dirección",[64,71,72],{},"Dónde estás",[74,75,76,92,104],"tbody",{},[61,77,78,82,85],{},[79,80,81],"td",{},"Ajustes de la plataforma: avisos de cookies, interruptores",[79,83,84],{},"De tu builder, en todas las aplicaciones que publica",[79,86,87],{},[88,89,91],"key-verdict",{"type":90},"safe","Nada que hacer",[61,93,94,97,100],{},[79,95,96],{},"Tus propios datos públicos: una lista de precios, un artículo",[79,98,99],{},"Tuya, y pensada para poder leerse",[79,101,102],{},[88,103,91],{"type":90},[61,105,106,109,112],{},[79,107,108],{},"Filas sobre personas: nombres, correos, pedidos, mensajes",[79,110,111],{},"Tuya, y accesible para cualquiera que tenga la dirección",[79,113,114],{},[88,115,117],{"type":116},"danger","Ciérrala hoy",[10,119,120],{},"La primera fila es un tablón que colgó otro. La segunda es un tablón que\ncolgaste tú a propósito. La tercera es el archivador, y lleva abierto tanto\ntiempo como lleva existiendo la dirección.",[22,122,124],{"id":123},"cómo-comprobar-qué-devuelven-tus-endpoints","Cómo comprobar qué devuelven tus endpoints",[10,126,127],{},"Abre cada uno desconectado y lee lo que llega. Leer lleva más tiempo del que\nsugiere el hallazgo, y es el único paso que responde a la pregunta.",[10,129,130,133,134,137],{},[17,131,132],{},"Encuentra las direcciones."," Abre tu aplicación en producción, pulsa F12 para\nabrir las herramientas de desarrollo del navegador, haz clic en ",[17,135,136],{},"Red"," y\nrecarga la página. Repasa las peticiones que vuelven como JSON. Esas son las\ndirecciones de las que tu aplicación saca datos, y la lista del informe es un\nsubconjunto de ellas.",[10,139,140,143],{},[17,141,142],{},"Pregunta a cada una como un desconocido."," Copia una URL, abre una ventana de\nnavegación privada para estar desconectado, y pégala. Lo que ves es exactamente\nlo que ve en esa dirección cualquiera en internet.",[10,145,146,149],{},[17,147,148],{},"Lee las primeras líneas."," Tres cosas pueden volver:",[151,152,153,162,165],"ul",{},[154,155,156,157,161],"li",{},"Un error, un rechazo o ",[158,159,160],"code",{},"[]",". La dirección quiere un login, o no hay nada ahí\npara alguien desconectado. Sigue.",[154,163,164],{},"Ajustes. Flags, interruptores, un color, una lista de funciones activadas, un\nnúmero de versión. No se nombra a nadie y no se describe nada. Sigue.",[154,166,167],{},"Filas. Un correo, el nombre de una persona, el importe de un pedido, el texto\nde un mensaje, un teléfono. Párate aquí, esta es la buena.",[10,169,170,171,174,175,178],{},"Si encuentras filas, prueba a cambiar un número de la URL antes de cerrar la\npestaña. Una dirección que entrega el registro de una persona con ",[158,172,173],{},"id=1"," y el de\notra distinta con ",[158,176,177],{},"id=2"," los está entregando todos, y eso conviene saberlo antes\nde decidir cuán urgente es esto.",[10,180,181,182,187],{},"Nuestro escaneo gratuito hace los dos primeros pasos por ti: lee en el código de\ntu aplicación las direcciones a las que llama, pide cada una sin login y lista\nlas que respondieron con datos. Tarda unos 20 segundos y no necesita cuenta:\n",[183,184,186],"a",{"href":185},"\u002Fsecurity-scanner","escanea tu aplicación",". El tercer paso es tuyo, porque eres\nla única persona que sabe si un nombre de esa lista es un cliente real.",[22,189,191],{"id":190},"la-mayoría-de-los-que-encontramos-son-de-la-plataforma","La mayoría de los que encontramos son de la plataforma",[10,193,194],{},"En nuestro barrido de agosto de 2026, 3.852 de las 30.926 aplicaciones en las\nque esta comprobación pudo obtener respuesta tenían al menos una dirección que\nresponde sin login. De ellas, 2.705 eran aplicaciones de Base44, y en Base44 la\ndirección es casi siempre la misma.",[10,196,197,198,201],{},"Es ",[158,199,200],{},"\u002Fapi\u002Fconsent\u002Fconfig",", los ajustes de cookies de la plataforma. En septiembre\nvolvimos a abrir una muestra de aplicaciones de Base44 señaladas y la única\ndirección que respondió fue esa. Devuelve ajustes, es idéntica en todas las\naplicaciones, y ahí dentro no hay datos de nadie. El escáner la está informando\ncorrectamente y el propietario no tiene nada que arreglar.",[55,203,204,217],{},[58,205,206],{},[61,207,208,211,214],{},[64,209,210],{},"Plataforma",[64,212,213],{},"Aplicaciones con el hallazgo",[64,215,216],{},"Aplicaciones en las que la comprobación respondió",[74,218,219,230,241,252,263],{},[61,220,221,224,227],{},[79,222,223],{},"Base44",[79,225,226],{},"2.705",[79,228,229],{},"5.419",[61,231,232,235,238],{},[79,233,234],{},"Dominios propios",[79,236,237],{},"82",[79,239,240],{},"955",[61,242,243,246,249],{},[79,244,245],{},"Bolt",[79,247,248],{},"4",[79,250,251],{},"1.120",[61,253,254,257,260],{},[79,255,256],{},"Lovable",[79,258,259],{},"8",[79,261,262],{},"18.518",[61,264,265,268,271],{},[79,266,267],{},"v0",[79,269,270],{},"0",[79,272,273],{},"1.786",[10,275,276,277,281],{},"Falta una plataforma en esa tabla a propósito. A Replit le corresponde un bloque\ngrande de los hallazgos restantes, y nadie ha vuelto a abrir una muestra de\nellos como hicimos con los de Base44, así que todavía no sabemos de quién son\nesas direcciones. Publicar una cifra como problema del propietario antes de que\nalguien la haya mirado es justo el camino por el que un ajuste de plataforma de\nBase44 se convierte en una estadística sobre propietarios negligentes. Todas las\ncifras de arriba salen de\n",[183,278,280],{"href":279},"\u002Fresearch\u002Fvibe-coded-app-security-2026","nuestro escaneo de 30.998 aplicaciones vibe-coded en producción",",\nque publica cada una junto a la base sobre la que se midió.",[10,283,284,285,288,289,293],{},"Lee juntos los dos extremos de esa tabla, porque la diferencia es la parte útil.\nEn Lovable son 8 aplicaciones de 18.518, y en v0 ninguna. Esas aplicaciones no\ntienen un pequeño servidor propio: la página habla con Supabase directamente\ndesde el navegador, así que no hay ningún ",[158,286,287],{},"\u002Fapi\u002F…"," tuyo en ninguna parte que\nesta comprobación pueda encontrar. Lo que protege los datos en una aplicación\nasí son las reglas de cada tabla, y ese es otro hallazgo con una\n",[183,290,292],{"href":291},"\u002Fblog\u002Fsupabase-rls-on-but-table-still-public","forma propia de salir mal",".",[22,295,297],{"id":296},"cuándo-el-json-son-ajustes-y-cuándo-son-personas","Cuándo el JSON son ajustes y cuándo son personas",[10,299,300],{},"Una pregunta lo decide: ¿algo en la respuesta describe a una persona?",[10,302,303],{},"Los ajustes describen tu aplicación. Un flag que dice si el modo oscuro está\nactivado, la lista de monedas que aceptas, la versión de algo, el texto de un\nbanner de cookies. Publicar eso revela cómo está configurada tu aplicación, y tu\naplicación funciona a la vista de todos de todas formas.",[10,305,306],{},"Las filas describen a tus usuarios. Un nombre, un correo, lo que alguien compró,\nlo que escribió, dónde vive. Eso no es una frase sobre configuración, es el\ncontenido del archivador, y importa por lo corriente que resulta llevárselo. No\nhay allanamiento. La dirección es una línea de texto que funciona en cualquier\nnavegador, así que acaba pegada en un chat de grupo, guardada en las notas de\nalguien y descargada a intervalos por un script que nadie vigila.",[36,308],{"alt":309,"caption":310,"src":311},"Dos respuestas de idéntico tamaño y forma. La de la izquierda contiene cuatro interruptores, dos encendidos y dos apagados. La de la derecha contiene cuatro filas, cada una con la figura de una persona y una línea tachada al lado.","Las dos son un 200 y un bloque de JSON, y eso es todo lo que la comprobación ve. La diferencia entre ellas es toda la pregunta, y puedes responderla leyendo.","\u002Fblog\u002Fopen-api-endpoint-exposed\u002Fsettings-or-people-1600x660.png",[313,314,316],"callout",{"type":315},"note",[10,317,318,321],{},[17,319,320],{},"El caso incómodo es la dirección que antes eran ajustes."," Un endpoint que\ndevolvía la lista de categorías de tu aplicación era dato público el día en que\nse escribió. Luego alguien añadió una columna a la tabla de detrás para que la\npágina pudiera mostrar quién creó cada categoría, y esa misma dirección empezó a\nentregar nombres. En pantalla no cambió nada, la dirección no se movió, y el\nhallazgo de tu informe se lee exactamente igual que antes.",[22,323,325],{"id":324},"cerrar-uno-y-por-qué-renombrar-la-ruta-no-lo-cierra","Cerrar uno, y por qué renombrar la ruta no lo cierra",[10,327,328],{},"Exige un login en la dirección, y responde con un 401 a quien llame sin tenerlo.",[10,330,331],{},"Ese es todo el arreglo, y va en la dirección y en ningún otro sitio. Tres cosas\nque parecen el arreglo y no lo son:",[151,333,334,340,350],{},[154,335,336,339],{},[17,337,338],{},"Renombrar la ruta, o sacarla de tu código."," Con esta hay que tener cuidado,\nporque el hallazgo desaparece. Quien ya tenía la URL la sigue teniendo, está\nen historiales de navegación y en registros de servidor, y el archivador sigue\nabierto, solo que sin la etiqueta delante.",[154,341,342,345,346,293],{},[17,343,344],{},"Estrechar tu cabecera CORS."," Eso decide qué otras webs pueden leer una\nrespuesta dentro del navegador de alguien, y nada más la consulta. Un script o\nun terminal lee la misma dirección de todos modos, y por eso\n",[183,347,349],{"href":348},"\u002Fblog\u002Fcors-wildcard-security-risk","el hallazgo del comodín es otra pregunta",[154,351,352,355],{},[17,353,354],{},"Filtrar en la aplicación."," Si la página esconde las filas que no debería\nmostrar, la dirección las sigue enviando. El filtrado tiene que ocurrir antes\nde que salga la respuesta.",[10,357,358],{},"Hay una cuarta opción, y en una página pública suele ser la mejor: dejar de\ntener el endpoint. Si los datos son solo para tu propia página, la página puede\nrecibirlos mientras se construye, y la dirección sale de tu código con ellos.",[22,360,362],{"id":361},"qué-aspecto-tiene-el-arreglo-en-código","Qué aspecto tiene el arreglo en código",[10,364,365],{},"Dos cosas, en el handler que responde a la dirección: averiguar quién pregunta,\ny parar si la respuesta es nadie.",[10,367,368],{},"Puede que nunca escribas esto tú, y no pasa nada. Léelo de todos modos, porque\nes con lo que compruebas que tu builder hizo de verdad lo que le pediste. Esta\nes la forma de antes, y es lo que encontró el escaneo:",[370,371,376],"pre",{"className":372,"code":374,"language":375},[373],"language-text","app.get('\u002Fapi\u002Forders', async (req, res) => {\n  const orders = await db.orders.findMany()\n  res.json(orders)\n})\n","text",[158,377,374],{"__ignoreMap":378},"",[10,380,381],{},"Y después:",[370,383,386],{"className":384,"code":385,"language":375},[373],"app.get('\u002Fapi\u002Forders', async (req, res) => {\n  const user = await getUserFromRequest(req)\n  if (!user) {\n    return res.status(401).json({ error: 'Not signed in' })\n  }\n\n  const orders = await db.orders.findMany({ where: { userId: user.id } })\n  res.json(orders)\n})\n",[158,387,385],{"__ignoreMap":378},[10,389,390,391,394,395,398],{},"Cambiaron dos cosas y las dos importan. El ",[158,392,393],{},"401"," es la puerta. El ",[158,396,397],{},"where"," es la\nrazón por la que la puerta merece la pena: sin él un visitante con sesión puede\nleer los pedidos de todos los demás clientes, que es un público más pequeño para\nlos mismos datos y sigue siendo el equivocado.",[10,400,401,404,405,408,409,412,413,416,417,420],{},[17,402,403],{},"En las Edge Functions de Supabase, lee la configuración antes de escribir\nnada."," Las Edge Functions comprueban el token de quien llama por su cuenta: la\nreferencia de configuración de Supabase pone ",[158,406,407],{},"verify_jwt"," en ",[158,410,411],{},"true"," por defecto,\nasí que una función que responde a desconocidos es una en la que alguien lo\ndesactivó, con una línea en ",[158,414,415],{},"supabase\u002Fconfig.toml"," o desplegando con\n",[158,418,419],{},"--no-verify-jwt",":",[370,422,425],{"className":423,"code":424,"language":375},[373],"[functions.orders]\nverify_jwt = false\n",[158,426,424],{"__ignoreMap":378},[10,428,429],{},"Si tu función debe ser privada, borra esas dos líneas y vuelve a desplegar.\nDéjalas solo donde el endpoint tenga que responder a cualquiera de verdad, que\nes el caso que la propia documentación de Supabase usa como ejemplo: un webhook\nde pagos. Dentro de la función, el SDK actual te da un cliente ya acotado a la\npersona que llamó:",[370,431,434],{"className":432,"code":433,"language":375},[373],"import { withSupabase } from 'npm:@supabase\u002Fserver'\n\nexport default {\n  fetch: withSupabase({ auth: 'user' }, async (_req, ctx) => {\n    const { data } = await ctx.supabase.from('orders').select()\n    return Response.json(data)\n  }),\n}\n",[158,435,433],{"__ignoreMap":378},[10,437,438,441,442,293],{},[158,439,440],{},"ctx.supabase"," se ejecuta como quien llamó, así que tus reglas de seguridad a\nnivel de fila deciden lo que vuelve, que es la misma protección de la que\ndepende el resto de tu aplicación y\n",[183,443,444],{"href":291},"lo que conviene comprobar aparte",[446,447,449],"h3",{"id":448},"el-prompt-para-pegar-en-tu-builder","El prompt para pegar en tu builder",[10,451,452],{},"Si construyes en Lovable, Bolt, Cursor, Replit o v0, dale esto. Déjalo en\ninglés, en el idioma en que estés leyendo esto, porque ese es el idioma en el\nque razonan estas herramientas, y sustituye las dos partes entre corchetes por\nlo que diga tu propio informe:",[370,454,457],{"className":455,"code":456,"language":375},[373],"A security scan of [my-app.com] found these API endpoints answer\nwithout any authentication: [\u002Fapi\u002Forders, \u002Fapi\u002Fusers].\n\nPlease look at each one and decide whether it is meant to be public.\nFor the ones that are not, require a valid session or API token and\nreturn 401 otherwise. Make sure each one returns only the rows that\nbelong to the person asking, not the whole table filtered in the UI.\nFor any that must stay open, make sure they return only non-sensitive\ndata and add rate limiting so they cannot be abused.\n\nTell me what you changed for each endpoint, and do not rename or\nremove the paths.\n",[158,458,456],{"__ignoreMap":378},[10,460,461],{},"Esa última frase está ahí a propósito. Cuando se le pide a un modelo que haga\ndesaparecer un hallazgo, a veces coge el camino más corto y mueve la dirección,\nque es el único resultado que parece un éxito al volver a escanear y no cambia\nnada.",[313,463,465],{"type":464},"warn",[10,466,467,470],{},[17,468,469],{},"Vuelve a escanear desconectado, no con sesión iniciada."," Una vez desplegado\nel arreglo, compruébalo como describe la sección de arriba: una ventana de\nnavegación privada y la URL. Probarlo con la sesión iniciada demuestra que el\nendpoint te sigue funcionando a ti, cosa que nunca estuvo en duda. Cierra\nsesión y léelo otra vez.",[22,472,474],{"id":473},"qué-hacer-ahora-mismo","Qué hacer ahora mismo",[476,477,478],"key-takeaways",{},[151,479,480,483,486,489,492],{},[154,481,482],{},"Abre cada dirección señalada en una ventana de navegación privada y lee lo que vuelve. Ese es el paso que el informe no puede dar por ti.",[154,484,485],{},"Si la respuesta son ajustes y la dirección es una que tú nunca escribiste, es de tu builder y no hay nada que arreglar. Busca la ruta en la documentación de tu plataforma antes de gastar nada en ella.",[154,487,488],{},"Si la respuesta contiene un nombre, un correo o un pedido, exige hoy mismo un login en esa dirección y devuelve un 401 a quien no lo tenga.",[154,490,491],{},"No renombres la ruta ni borres la referencia a ella. El hallazgo desaparece y la dirección sigue respondiendo.",[154,493,494],{},"Prueba a cambiar un id en la URL de tu propia aplicación. Una dirección que sirve un registro a un desconocido suele servirlos todos.",[10,496,497,498,293],{},"Las direcciones de esta lista las escribió alguien que sabía qué había detrás en\naquel momento, y lo que cambia es lo que hay detrás. Reeve Care vuelve a pasar\neste escaneo por tu aplicación en producción de forma periódica y te envía un\ncorreo cuando un resultado empeora, que es justo el caso que el repaso\ndesconectado de arriba no puede pillar:\n",[183,499,501],{"href":500},"\u002Fpricing","qué vigila y cuánto cuesta",[10,503,504,505,509,510,514],{},"Si prefieres trabajarlo todo de una sentada, la\n",[183,506,508],{"href":507},"\u002Fchecklist","lista de seguridad en 10 minutos"," cubre esto junto al resto de lo\nque una aplicación recién lanzada suele dejar abierto, y\n",[183,511,513],{"href":512},"\u002Fblog\u002Fwhich-api-keys-are-safe-in-your-frontend","qué claves son seguras en tu frontend","\nes el otro hallazgo con el que la gente suele llegar.",{"title":378,"searchDepth":516,"depth":516,"links":517},3,[518,520,521,522,523,524,525,528],{"id":24,"depth":519,"text":25},2,{"id":46,"depth":519,"text":47},{"id":123,"depth":519,"text":124},{"id":190,"depth":519,"text":191},{"id":296,"depth":519,"text":297},{"id":324,"depth":519,"text":325},{"id":361,"depth":519,"text":362,"children":526},[527],{"id":448,"depth":516,"text":449},{"id":473,"depth":519,"text":474},"Fundamentos de seguridad","\u002Fblog\u002Fopen-api-endpoint-exposed\u002Fcover-1200x630.png","Dos ventanillas idénticas en una pared, de cada una sale una hoja. Una lleva una fila de interruptores, la otra una columna de caras.","Tu escaneo señaló un endpoint de API abierto. Que importe depende de lo que devolvió, y la mayoría de los que encontramos eran de la plataforma.",false,"md",[536,539,541,544,547],{"q":537,"a":538},"¿Qué significa \"endpoint de API abierto\" en mi informe?","Significa que leímos el código que tu aplicación envía a un navegador, encontramos dentro una dirección a la que tu aplicación pide datos, y le pedimos datos a esa dirección sin cuenta y sin login. Respondió, y la respuesta era JSON. Ese es todo el hallazgo. Recoge lo que pasó, y no juzga si los datos eran privados, porque nada fuera de tu aplicación puede saberlo.",{"q":47,"a":540},"No. Muchas direcciones están pensadas para responder a cualquiera: una lista de precios publicada, un artículo, una lista de qué funciones están activadas. El problema es la dirección que devuelve filas que pertenecen a personas, porque lo hace para todo el que tenga la URL. Lee lo que volvió y pregúntate si algo de eso describe a una persona.",{"q":542,"a":543},"Mi informe señala un endpoint que yo no escribí. ¿Qué es?","Normalmente es de tu builder. Las plataformas que le dan a tu aplicación un pequeño servidor propio también le meten unas cuantas direcciones suyas, y esas están en el código de todas las aplicaciones de la plataforma. El ejemplo más claro que hemos medido son los ajustes de cookies de Base44 en \u002Fapi\u002Fconsent\u002Fconfig, que suponen 2.705 de los 3.852 hallazgos de nuestro barrido de agosto de 2026. Devuelven ajustes, son idénticos en todas las aplicaciones de Base44, y nada de lo que hay ahí te pertenece.",{"q":545,"a":546},"¿Cómo compruebo qué devuelven los endpoints de mi aplicación?","Abre tu aplicación en producción, pulsa F12, haz clic en Red y recarga. Las peticiones que vuelven como JSON son las direcciones de las que tu aplicación saca datos. Copia cada URL, abre una ventana de navegación privada para estar desconectado, y pégalas de una en una. Lee lo que llega. Un error o una lista vacía está bien. Las filas con nombres, correos o importes de pedidos las puede leer cualquiera que tenga esa dirección.",{"q":548,"a":549},"¿Esconder la ruta lo arregla?","No. Renombrar la dirección, o quitar la referencia a ella de tu código, impide que un escáner la encuentre y la deja respondiendo igual que antes. Quien ya tenía la URL la sigue teniendo, y permanece en historiales de navegación, registros de servidor y todo lo que haya rastreado tu sitio. Se arregla en la dirección: exigir un login y responder a un desconocido con un 401.","\u002Fblog\u002Fopen-api-endpoint-exposed\u002Fcard-800x500.png",[552,553,554,555,556,557,558],"endpoint de api abierto","endpoint de api sin autenticación","endpoint de api devuelve datos sin login","mi endpoint de api es público","ruta de api desprotegida","endpoint de api sin autenticar","endpoint json expuesto",{},true,"¿Un endpoint de API abierto es un problema de seguridad?","\u002Fblog\u002Fopen-api-endpoint-exposed","2026-09-25",{"title":5,"description":532},"blog\u002Fopen-api-endpoint-exposed",[567,568,569],"Un endpoint de API abierto significa que una ruta escrita en el código de tu aplicación respondió a una petición sin login, y lo que volvió fue JSON.","De los que encontramos en 30.926 aplicaciones, siete de cada diez los puso la plataforma en la que se publicó la aplicación, y devuelven ajustes.","La pregunta que lo decide es si algo en la respuesta describe a una persona. Una lista de precios es pública a propósito. Una lista de tus clientes nunca lo fue.","NaFuRh9W9KHwsIGI-uCf5rZyWM0zyOF81eXyYL7Pu4U",[572,578,584,590,596,602,608,614,620,626,632,638,644,651,657,663,668,669,675,681,687,693,699,705,711,717,723,729,735,741,747,753,759,765,771,777,783,789,795,801,807,813,819,825,831,837,843,848,854,860,866,872,878,884,889,895,901,906,911,917],{"path":573,"title":574,"description":575,"published":576,"category":529,"image":577,"draft":533},"\u002Fblog\u002Fbase44-security-scan","Escaneo de seguridad de Base44: lo que solo él ve","El escaneo de seguridad de Base44 revisa siete tipos de problema desde dentro. Esta es la mitad que lee y nadie más puede leer, y la mitad que nunca mira.","2026-10-10","\u002Fblog\u002Fbase44-security-scan\u002Fcard-800x500.png",{"path":579,"title":580,"description":581,"published":582,"category":529,"image":583,"draft":533},"\u002Fblog\u002Fapi-key-leaked-what-to-do","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.","2026-10-09","\u002Fblog\u002Fapi-key-leaked-what-to-do\u002Fcard-800x500.png",{"path":585,"title":586,"description":587,"published":588,"category":529,"image":589,"draft":533},"\u002Fblog\u002Finfinite-recursion-in-policy-for-relation","\"Infinite recursion detected in policy\" sin desactivar RLS","\"Infinite recursion detected in policy for relation\" significa que tu policy preguntó a la tabla que protege. Así se rompe el círculo.","2026-10-08","\u002Fblog\u002Finfinite-recursion-in-policy-for-relation\u002Fcard-800x500.png",{"path":591,"title":592,"description":593,"published":594,"category":529,"image":595,"draft":533},"\u002Fblog\u002Fno-api-key-found-in-request","\"No API key found in request\" en Supabase, y el arreglo equivocado","\"No API key found in request\" significa que tu petición llegó a Supabase sin clave. Casi todas las respuestas apuntan a las reglas de tu base de datos.","2026-10-07","\u002Fblog\u002Fno-api-key-found-in-request\u002Fcard-800x500.png",{"path":597,"title":598,"description":599,"published":600,"category":529,"image":601,"draft":533},"\u002Fblog\u002Fis-base44-safe","¿Es seguro Base44? Lo que vimos en 5.442 apps Base44","¿Es seguro Base44? Pasamos nueve comprobaciones a 5.442 apps Base44 en producción. Casi todas sacaron B por motivos que su dueño nunca eligió.","2026-10-06","\u002Fblog\u002Fis-base44-safe\u002Fcard-800x500.png",{"path":603,"title":604,"description":605,"published":606,"category":529,"image":607,"draft":533},"\u002Fblog\u002Fmove-a-secret-to-an-edge-function","Ocultar una clave API: muévela a una Edge Function de Supabase","Ocultar una clave API significa sacarla del navegador, y una Edge Function de Supabase es el sitio más pequeño donde ponerla. Dos pasos importan más.","2026-10-05","\u002Fblog\u002Fmove-a-secret-to-an-edge-function\u002Fcard-800x500.png",{"path":609,"title":610,"description":611,"published":612,"category":529,"image":613,"draft":533},"\u002Fblog\u002Fenv-file-exposed-on-your-server","¿Tu archivo .env está expuesto? Las doce rutas a revisar","¿Tu archivo .env está expuesto en tu propio servidor web? Doce direcciones te lo dicen, y un acierto significa que todo lo que contiene ya es público.","2026-10-04","\u002Fblog\u002Fenv-file-exposed-on-your-server\u002Fcard-800x500.png",{"path":615,"title":616,"description":617,"published":618,"category":529,"image":619,"draft":533},"\u002Fblog\u002Fis-v0-safe","Seguridad de v0: las 1.790 aplicaciones escaneadas sacaron una A","Seguridad de v0, medida en 1.790 aplicaciones v0 en vivo: todas sacaron A. Solo 17 nombraban una base de datos, y eso es casi todo lo que mide la A.","2026-10-03","\u002Fblog\u002Fis-v0-safe\u002Fcard-800x500.png",{"path":621,"title":622,"description":623,"published":624,"category":529,"image":625,"draft":533},"\u002Fblog\u002Fis-bolt-safe","¿Es seguro Bolt? Lo que vimos en 1.123 aplicaciones Bolt","¿Es seguro Bolt? Pasamos 1.123 aplicaciones Bolt en vivo por nueve comprobaciones. El hosting salió limpio. Los hallazgos eran claves y tablas abiertas dentro.","2026-10-02","\u002Fblog\u002Fis-bolt-safe\u002Fcard-800x500.png",{"path":627,"title":628,"description":629,"published":630,"category":529,"image":631,"draft":533},"\u002Fblog\u002Fis-supabase-down-or-your-app","¿Supabase está caído o es tu app? Cómo distinguirlo","¿Supabase está caído o es tu app? Un proyecto pausado, una base de datos llena y un fallo tuyo parecen lo mismo. Cuatro comprobaciones los separan.","2026-10-01","\u002Fblog\u002Fis-supabase-down-or-your-app\u002Fcard-800x500.png",{"path":633,"title":634,"description":635,"published":636,"category":529,"image":637,"draft":533},"\u002Fblog\u002Fdoes-supabase-encrypt-my-data","¿Supabase cifra mis datos? Sí. Esto es lo que detiene","¿Supabase cifra los datos? Sí: AES-256 en reposo, TLS en tránsito, SOC 2 e ISO 27001. Qué cubre cada uno, y la fuga que ninguno de ellos detiene.","2026-09-30","\u002Fblog\u002Fdoes-supabase-encrypt-my-data\u002Fcard-800x500.png",{"path":639,"title":640,"description":641,"published":642,"category":529,"image":643,"draft":533},"\u002Fblog\u002Fsupabase-free-plan-limits","¿Cuánto cuesta Supabase? Los límites del plan gratuito","Los límites del plan gratuito de Supabase: 500 MB de base de datos, 5 GB de egress y ningún backup. Qué pasa en cada uno y qué cambia Pro por $25.","2026-09-29","\u002Fblog\u002Fsupabase-free-plan-limits\u002Fcard-800x500.png",{"path":645,"title":646,"description":647,"published":648,"category":649,"image":650,"draft":533},"\u002Fblog\u002Ftest-your-supabase-backup","Prueba tu backup de Supabase antes del día que lo necesites","Cómo probar tu backup de Supabase: restaurarlo en un proyecto de prueba, contar filas, iniciar sesión y buscar la línea que le falta a un archivo cortado.","2026-09-28","Backups","\u002Fblog\u002Ftest-your-supabase-backup\u002Fcard-800x500.png",{"path":652,"title":653,"description":654,"published":655,"category":649,"image":656,"draft":533},"\u002Fblog\u002Fsupabase-backup-github-action","Backup gratis de Supabase con una GitHub Action, y la trampa","Un backup de Supabase con GitHub Action es gratis y sirve a muchas apps. El workflow, el connection string correcto y el egress de cada ejecución.","2026-09-27","\u002Fblog\u002Fsupabase-backup-github-action\u002Fcard-800x500.png",{"path":658,"title":659,"description":660,"published":661,"category":649,"image":662,"draft":533},"\u002Fblog\u002Fdownload-your-supabase-backup","Por qué no puedes descargar tu backup de Supabase","No puedes descargar tu backup de Supabase en un proyecto actual: la copia diaria es una instantánea física. Cómo comprobarlo y tener tu propia copia.","2026-09-26","\u002Fblog\u002Fdownload-your-supabase-backup\u002Fcard-800x500.png",{"path":664,"title":665,"description":666,"published":661,"category":529,"image":667,"draft":533},"\u002Fblog\u002Fsupabase-permission-denied-for-table","Supabase \"permission denied for table\": el grant que falta","Desde el 30 de octubre, una tabla nueva de Supabase responde \"permission denied for table\" hasta que le das acceso. El grant del email es la mitad del arreglo.","\u002Fblog\u002Fsupabase-permission-denied-for-table\u002Fcard-800x500.png",{"path":562,"title":5,"description":532,"published":563,"category":529,"image":550,"draft":533},{"path":670,"title":671,"description":672,"published":673,"category":529,"image":674,"draft":533},"\u002Fblog\u002Flovable-security-scan","Escaneo de seguridad de Lovable: lo que no puede probar","Escaneo de seguridad de Lovable: qué revisan el Quick scan y el Deep scan, cuándo corre cada uno, y lo único que ningún escaneo desde dentro puede probar.","2026-09-24","\u002Fblog\u002Flovable-security-scan\u002Fcard-800x500.png",{"path":676,"title":677,"description":678,"published":679,"category":649,"image":680,"draft":533},"\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","\u002Fblog\u002Fsupabase-backup-auth-users\u002Fcard-800x500.png",{"path":682,"title":683,"description":684,"published":685,"category":529,"image":686,"draft":533},"\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":688,"title":689,"description":690,"published":691,"category":529,"image":692,"draft":533},"\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.","2026-09-21","\u002Fblog\u002Fis-lovable-safe\u002Fcard-800x500.png",{"path":694,"title":695,"description":696,"published":697,"category":529,"image":698,"draft":533},"\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":700,"title":701,"description":702,"published":703,"category":529,"image":704,"draft":533},"\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":706,"title":707,"description":708,"published":709,"category":529,"image":710,"draft":533},"\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":712,"title":713,"description":714,"published":715,"category":529,"image":716,"draft":533},"\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":718,"title":719,"description":720,"published":721,"category":529,"image":722,"draft":533},"\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":724,"title":725,"description":726,"published":727,"category":529,"image":728,"draft":533},"\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":730,"title":731,"description":732,"published":733,"category":649,"image":734,"draft":533},"\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":736,"title":737,"description":738,"published":739,"category":649,"image":740,"draft":533},"\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":742,"title":743,"description":744,"published":745,"category":649,"image":746,"draft":533},"\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":748,"title":749,"description":750,"published":751,"category":529,"image":752,"draft":533},"\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":754,"title":755,"description":756,"published":757,"category":529,"image":758,"draft":533},"\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":760,"title":761,"description":762,"published":763,"category":529,"image":764,"draft":533},"\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":766,"title":767,"description":768,"published":769,"category":529,"image":770,"draft":533},"\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":772,"title":773,"description":774,"published":775,"category":529,"image":776,"draft":533},"\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":778,"title":779,"description":780,"published":781,"category":529,"image":782,"draft":533},"\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":784,"title":785,"description":786,"published":787,"category":529,"image":788,"draft":533},"\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":790,"title":791,"description":792,"published":793,"category":529,"image":794,"draft":533},"\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":796,"title":797,"description":798,"published":799,"category":529,"image":800,"draft":533},"\u002Fblog\u002Fopenai-api-key-exposed-in-frontend","Tu clave de API de OpenAI está expuesta en tu frontend. Rótala.","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.","2026-09-03","\u002Fblog\u002Fopenai-api-key-exposed-in-frontend\u002Fcard-800x500.png",{"path":802,"title":803,"description":804,"published":805,"category":529,"image":806,"draft":533},"\u002Fblog\u002Fwhat-secrets-leak-from-vibe-coded-apps","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":808,"title":809,"description":810,"published":811,"category":649,"image":812,"draft":533},"\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":814,"title":815,"description":816,"published":817,"category":529,"image":818,"draft":533},"\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":820,"title":821,"description":822,"published":823,"category":649,"image":824,"draft":533},"\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":826,"title":827,"description":828,"published":829,"category":529,"image":830,"draft":533},"\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":832,"title":833,"description":834,"published":835,"category":529,"image":836,"draft":533},"\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":838,"title":839,"description":840,"published":841,"category":529,"image":842,"draft":533},"\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":348,"title":844,"description":845,"published":846,"category":529,"image":847,"draft":533},"¿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":849,"title":850,"description":851,"published":852,"category":649,"image":853,"draft":533},"\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":855,"title":856,"description":857,"published":858,"category":649,"image":859,"draft":533},"\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":861,"title":862,"description":863,"published":864,"category":529,"image":865,"draft":533},"\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":867,"title":868,"description":869,"published":870,"category":529,"image":871,"draft":533},"\u002Fblog\u002Fgoogle-api-key-exposed-in-frontend","¿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":873,"title":874,"description":875,"published":876,"category":649,"image":877,"draft":533},"\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":879,"title":880,"description":881,"published":882,"category":529,"image":883,"draft":533},"\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":885,"title":886,"description":887,"published":882,"category":529,"image":888,"draft":533},"\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":890,"title":891,"description":892,"published":893,"category":529,"image":894,"draft":533},"\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":896,"title":897,"description":898,"published":899,"category":649,"image":900,"draft":533},"\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":291,"title":902,"description":903,"published":904,"category":529,"image":905,"draft":533},"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":907,"title":908,"description":909,"published":904,"category":649,"image":910,"draft":533},"\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":912,"title":913,"description":914,"published":915,"category":649,"image":916,"draft":533},"\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":512,"title":918,"description":919,"published":915,"category":529,"image":920,"draft":533},"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",1791618971507]