01 row-level security
Row-Level Security (RLS)
Imagínalo así
Imagina una caja de juguetes gigante donde todos los niños del salón guardan sus juguetes. Sin candado, cualquier niño puede agarrar los juguetes de todos. Con Row-Level Security, le pones un candado mágico que sabe quién es cada niño — cuando uno abre la caja, solo ve sus propios juguetes. Los demás juguetes son invisibles para él.
¿Qué pasa sin RLS?
Ahorita, si alguien entra a tu app, puede ver la información de todos los usuarios: correos, datos privados, todo. Un usuario malicioso solo necesita cambiar un ID en la URL para ver los datos de otra persona. Es la vulnerabilidad más común en apps con Supabase y PostgreSQL.
Row-Level Security es una función de PostgreSQL (la base de datos que usa Supabase) que pone reglas directamente en la base de datos. En vez de confiar en que tu código filtre bien los datos, la base de datos automáticamente esconde las filas que no le pertenecen al usuario. Si tu código tiene un error, RLS sigue protegiendo.
Prompt para activar RLS con Claude
Copia este prompt, cámbialo con tus tablas y pégalo en Claude Code. Él genera todo el SQL.
Estoy usando Supabase con PostgreSQL. Tengo las siguientes tablas en mi base de datos: - users (id, email, name) - posts (id, user_id, title, content, created_at) - comments (id, post_id, user_id, content) (Cambia estas tablas por las tuyas) Necesito que configures Row-Level Security (RLS) para que: 1. Cada usuario solo pueda VER sus propios posts 2. Cada usuario solo pueda CREAR posts con su propio user_id 3. Cada usuario solo pueda EDITAR sus propios posts 4. Cada usuario solo pueda BORRAR sus propios posts 5. Los comentarios se pueden ver todos, pero solo editar/borrar los propios Por favor: - Activa RLS en TODAS las tablas con ALTER TABLE ... ENABLE ROW LEVEL SECURITY - Crea políticas SEPARADAS para SELECT, INSERT, UPDATE y DELETE (no uses FOR ALL) - Usa auth.uid() para referenciar al usuario autenticado - NUNCA uses user_metadata en las políticas (los usuarios pueden modificarlo) - Agrega índices en las columnas user_id para rendimiento - Muéstrame cómo probar las políticas desde el SDK de JavaScript de Supabase - Dame el SQL completo que pueda copiar y pegar en el SQL Editor de Supabase
- Crea dos usuarios de prueba en tu app
- Inicia sesión como Usuario A y crea datos
- Inicia sesión como Usuario B — no debes ver los datos de A
- Prueba siempre desde el SDK de JavaScript, no desde el SQL Editor (el editor salta las reglas de RLS)
02 cors
CORS (Cross-Origin Resource Sharing)
Imagínalo así
Tu aplicación tiene puertas donde entra y sale información. CORS es el guardia de seguridad en la puerta. Tiene una lista de quién puede entrar. Si alguien llega y no está en la lista, el guardia dice "no pasas". Sin guardia, cualquier desconocido puede entrar por la puerta y meter cosas raras en tu app.
¿Qué pasa sin CORS?
Sin CORS, alguien puede crear una página falsa que haga requests a tu API haciéndose pasar por tu app. Si tu usuario está logueado en una pestaña y visita la página del atacante en otra pestaña, el atacante puede robar su información, hacer cambios en su cuenta o acceder a datos privados — todo sin que el usuario se entere.
CORS es una regla del navegador que controla qué páginas web pueden hablar con tu API. Cuando tu frontend en miapp.com pide datos a api.miapp.com, el navegador revisa si la API permite ese origen. Si no lo permite, el navegador bloquea la respuesta automáticamente.
Prompt para configurar CORS con Claude
Cambia los dominios por los tuyos y tu stack. Claude configura todo.
Estoy construyendo una aplicación web. Mi frontend está en https://miapp.com y mi backend/API está en https://api.miapp.com (cambia estos dominios por los tuyos). Necesito que configures CORS correctamente para que: 1. SOLO acepte requests que vengan de mi dominio exacto (https://miapp.com) — NUNCA uses Access-Control-Allow-Origin: * 2. Permita los métodos GET, POST, PUT, DELETE 3. Permita los headers estándar más Authorization y Content-Type 4. Soporte cookies y tokens de autenticación (credentials) 5. Maneje correctamente los preflight requests (OPTIONS) Reglas importantes: - NO uses wildcards (*) en ningún header de CORS - NO copies el header Origin del request al response (eso es inseguro) - SOLO permite HTTPS, nunca HTTP - Si usas credentials: true, el origin TIENE que ser exacto (no puede ser *) También necesito que: - Me expliques qué es un preflight request y por qué importa - Me muestres cómo verificar que CORS está bien configurado usando el Network tab de DevTools - Me des un ejemplo de qué pasa cuando alguien intenta acceder desde un dominio no autorizado Mi stack es: [Next.js / Express / otro — pon el tuyo aquí]
Errores que NUNCA debes cometer
Usar * como origin
Cualquier página del mundo puede hacer requests a tu API
Reflejar el Origin del request
Un atacante manda su dominio y tu servidor lo acepta automáticamente
Mezclar * con credentials
Los navegadores lo bloquean, pero algunos developers lo fuerzan y abren la puerta
Permitir HTTP además de HTTPS
Alguien puede interceptar tu tráfico y robar datos en el camino
- Abre tu app en el navegador y ve a DevTools → pestaña Network
- Busca un request a tu API — debe tener el header
Access-Control-Allow-Origincon tu dominio exacto - Busca que NO diga
*— debe decir tu dominio - Si ves un request OPTIONS antes del request real, es el preflight — debe regresar 200
03 security headers
Security Headers
Imagínalo así
Los Security Headers son como letreros de seguridad en un parque de juegos. Uno dice "no te subas al techo" (X-Frame-Options). Otro dice "solo usa los juegos oficiales" (Content-Security-Policy). Otro dice "revisa el piso antes de jugar" (X-Content-Type-Options). Estos letreros le dicen al navegador las reglas de seguridad de tu página.
¿Qué pasa sin Security Headers?
Sin esta configuración, un atacante puede: meter tu página dentro de una página falsa para engañar a tus usuarios (clickjacking), inyectar código malicioso que robe datos (XSS), subir archivos disfrazados que el navegador ejecuta como código, o interceptar tu tráfico HTTP para robar información en el camino.
Los 6 headers que necesitas
X-Frame-Options
Evita que metan tu página dentro de una página falsa
SAMEORIGINContent-Security-Policy
Controla de dónde puede cargar scripts, estilos e imágenes
default-src 'self'...X-Content-Type-Options
Evita que el navegador ejecute archivos disfrazados
nosniffStrict-Transport-Security
Fuerza HTTPS siempre — nadie puede interceptar tu tráfico
max-age=31536000Referrer-Policy
Controla qué info se envía cuando el usuario sale de tu página
strict-origin-when-cross-originPermissions-Policy
Desactiva cámara, micrófono y ubicación si no los necesitas
geolocation=(), camera=()Prompt para agregar Security Headers con Claude
Cambia tu stack y dominio. Claude sabe exactamente dónde ponerlos.
Estoy construyendo una aplicación web con [Next.js / Express / otro — pon tu stack aquí]. Mi dominio es https://miapp.com. Necesito que agregues TODOS los Security Headers de producción: 1. X-Frame-Options: SAMEORIGIN → Para que nadie pueda meter mi página dentro de un iframe en otra página falsa (previene clickjacking) 2. Content-Security-Policy (CSP): → default-src 'self' — solo cargar recursos de mi propio dominio → script-src 'self' — solo scripts de mi dominio (nada de inline ni eval) → img-src 'self' https: — imágenes de mi dominio o cualquier HTTPS → style-src 'self' 'unsafe-inline' — estilos de mi dominio (unsafe-inline necesario para algunos frameworks) → connect-src 'self' https://api.miapp.com — solo llamadas API a mi backend → font-src 'self' — solo fuentes de mi dominio → object-src 'none' — bloquear <object> y <embed> 3. X-Content-Type-Options: nosniff → Para que el navegador no "adivine" qué tipo de archivo es (previene ataques con archivos disfrazados) 4. Strict-Transport-Security: max-age=31536000; includeSubDomains → Forzar HTTPS siempre durante 1 año completo 5. Referrer-Policy: strict-origin-when-cross-origin → Controlar qué información se envía cuando el usuario navega a otro sitio 6. Permissions-Policy: geolocation=(), microphone=(), camera=() → Desactivar acceso a cámara, micrófono y ubicación (a menos que los necesites) Por favor: - Configura todo en el archivo correcto de mi framework - Si uso Next.js, ponlo en next.config.js en la sección headers - Muéstrame cómo verificar que los headers están funcionando - Explícame qué hace cada header en una oración simple - Si uso servicios externos (Google Analytics, Stripe, etc.) dime qué ajustar en el CSP
- Ve a securityheaders.com y pon tu dominio — te da una calificación de A+ a F
- Abre DevTools → Network → clic en cualquier request → busca Response Headers
- Revisa la consola del navegador — si el CSP bloquea algo legítimo, te dice qué ajustar
- Si usas Google Analytics, Stripe u otro servicio externo, agrega sus dominios al CSP