Desarrollar aplicaciones web con inteligencia artificial aceleró radicalmente los tiempos de producción. Hoy es posible construir un sistema completo en horas utilizando herramientas como GitHub Copilot, Cursor, Claude o ChatGPT. Pero esta velocidad trae consigo un riesgo que muchos desarrolladores subestiman: la seguridad queda en segundo plano.
La IA genera código funcional con notable precisión, pero no siempre genera código seguro. Los modelos de lenguaje aprenden de millones de repositorios públicos, muchos de los cuales contienen vulnerabilidades conocidas, configuraciones inseguras por defecto o prácticas obsoletas. Cuando ese código llega a producción sin revisión, las consecuencias pueden ser graves.
En este artículo vas a encontrar tres medidas de seguridad fundamentales que debés aplicar en toda aplicación web desarrollada con asistencia de IA: Row Level Security (RLS), CORS y Security Headers. Cada una aborda una capa distinta del modelo de seguridad y, aplicadas en conjunto, reducen drásticamente la superficie de ataque de tu sistema.
¿Por Qué la IA Introduce Nuevos Riesgos de Seguridad?
Antes de entrar en las medidas concretas, es importante entender por qué las aplicaciones generadas con IA son especialmente vulnerables.
Cuando le pedís a un modelo de IA que te genere un endpoint REST, una consulta SQL o una configuración de servidor, el código resultante cumple con el objetivo funcional que describiste. El problema es que la IA optimiza para satisfacer tu pedido, no para anticipar escenarios de ataque que no mencionaste explícitamente.
Algunos patrones problemáticos que los modelos repiten frecuentemente:
- Queries SQL sin filtros de usuario que exponen datos de todos los registros
- APIs con CORS abierto (
Access-Control-Allow-Origin: *) por simplicidad - Servidores sin cabeceras de seguridad configuradas
- Autenticación implementada pero sin autorización a nivel de datos
- Variables de entorno hardcodeadas en ejemplos que luego se copian a producción
El desarrollador que utiliza IA como copiloto debe entender que la revisión de seguridad es responsabilidad humana, no del modelo. Con ese punto claro, veamos las tres medidas que marcan la diferencia.
1. Row Level Security (RLS): Protegé los Datos en la Capa de Base de Datos
¿Qué es Row Level Security?
Row Level Security (RLS), o seguridad a nivel de fila, es un mecanismo que permite controlar qué filas de una tabla puede leer, insertar, modificar o eliminar cada usuario, directamente desde el motor de base de datos, sin depender de la lógica de la aplicación.
En lugar de filtrar datos en el código del backend (lo que puede fallar si hay un error, un bypass o una ruta olvidada), RLS garantiza que la restricción vive en la base de datos misma. Aunque el código de tu aplicación tenga un bug, la base de datos nunca va a devolver datos que el usuario no tiene permitido ver.
¿Por Qué es Crítico en Aplicaciones con IA?
La IA suele generar queries como esta para obtener el perfil de un usuario:
SELECT * FROM usuarios WHERE id = $1;Esto parece correcto. Pero si en algún punto de la aplicación esa query se ejecuta sin el filtro adecuado, o si un atacante manipula el parámetro, podría acceder a datos de otros usuarios. RLS elimina ese riesgo desde la raíz.
Cómo Implementar RLS en PostgreSQL (con Supabase)
PostgreSQL y Supabase tienen soporte nativo para RLS. Así se activa y configura:
-- 1. Activar RLS en la tabla
ALTER TABLE documentos ENABLE ROW LEVEL SECURITY;
-- 2. Crear política: cada usuario solo ve sus propios documentos
CREATE POLICY "usuario_ve_sus_documentos"
ON documentos
FOR SELECT
USING (auth.uid() = user_id);
-- 3. Política para inserción: solo puede crear documentos propios
CREATE POLICY "usuario_crea_documentos"
ON documentos
FOR INSERT
WITH CHECK (auth.uid() = user_id);
-- 4. Política para actualización y eliminación
CREATE POLICY "usuario_edita_sus_documentos"
ON documentos
FOR UPDATE
USING (auth.uid() = user_id);
CREATE POLICY "usuario_elimina_sus_documentos"
ON documentos
FOR DELETE
USING (auth.uid() = user_id);Con estas políticas activas, incluso si un bug en la aplicación ejecuta SELECT * FROM documentos sin filtro de usuario, la base de datos solo va a retornar los registros del usuario autenticado.
RLS en MySQL y MariaDB
MySQL no tiene RLS nativo, pero se puede implementar con vistas y procedimientos almacenados. La alternativa más robusta en este ecosistema es asegurarse de que todos los accesos a datos se realicen a través de procedimientos almacenados que incluyan el filtro de usuario como parámetro obligatorio, o bien migrar a PostgreSQL si la seguridad es crítica.
Puntos Clave del RLS
- La seguridad vive en la base de datos, no solo en el código de la app
- Protege contra bugs de lógica en el backend
- Es independiente del framework o lenguaje que uses
- Supabase lo implementa de forma nativa con integración JWT
- Requiere que el contexto del usuario (ID, rol) sea accesible desde la sesión de base de datos
2. CORS: Controlá Quién Puede Hablar con Tu API
¿Qué es CORS?
CORS (Cross-Origin Resource Sharing) es el mecanismo que define qué dominios externos tienen permitido hacer solicitudes a tu API o servidor. Sin una configuración correcta, cualquier sitio web malicioso podría hacer peticiones a tu backend usando las credenciales del usuario autenticado.
El error más común que genera la IA es configurar CORS de forma permisiva para evitar errores durante el desarrollo:
// ❌ Configuración insegura generada por IA para "que funcione rápido"
app.use(cors({ origin: '*' }));Esto significa que cualquier sitio de internet puede hacer solicitudes autenticadas a tu API. En producción, esto es una vulnerabilidad crítica que facilita ataques CSRF y exfiltración de datos.
Configuración Correcta de CORS en Node.js / Express
const allowedOrigins = [
'https://tudominio.com',
'https://www.tudominio.com',
// Solo en desarrollo local:
process.env.NODE_ENV === 'development' ? 'http://localhost:3000' : null,
].filter(Boolean);
const corsOptions = {
origin: (origin, callback) => {
// Permitir requests sin origin (apps móviles, curl, Postman en dev)
if (!origin || allowedOrigins.includes(origin)) {
callback(null, true);
} else {
callback(new Error(`Origen no permitido por política CORS: ${origin}`));
}
},
credentials: true, // Necesario si usás cookies o sessions
methods: ['GET', 'POST', 'PUT', 'DELETE', 'PATCH'],
allowedHeaders: ['Content-Type', 'Authorization'],
maxAge: 86400, // Cache del preflight por 24 horas
};
app.use(cors(corsOptions));Configuración en PHP / Laravel
// config/cors.php en Laravel
return [
'paths' => ['api/*'],
'allowed_methods' => ['GET', 'POST', 'PUT', 'DELETE'],
'allowed_origins' => [
'https://tudominio.com',
'https://www.tudominio.com',
],
'allowed_origins_patterns' => [],
'allowed_headers' => ['Content-Type', 'Authorization', 'X-Requested-With'],
'exposed_headers' => [],
'max_age' => 86400,
'supports_credentials' => true,
];Configuración en Nginx (a nivel de servidor)
server {
listen 443 ssl;
server_name tudominio.com;
location /api/ {
# Solo permite el origen específico
if ($http_origin ~* "^https://(www\.)?tudominio\.com$") {
add_header 'Access-Control-Allow-Origin' "$http_origin" always;
add_header 'Access-Control-Allow-Credentials' 'true' always;
add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, DELETE, OPTIONS' always;
add_header 'Access-Control-Allow-Headers' 'Authorization, Content-Type' always;
}
# Manejar preflight OPTIONS
if ($request_method = 'OPTIONS') {
add_header 'Access-Control-Max-Age' 86400;
add_header 'Content-Length' 0;
return 204;
}
proxy_pass http://localhost:3000;
}
}Errores Comunes con CORS
- Usar
*concredentials: true: los navegadores lo bloquean, y al «arreglarlo» muchos ponen el origen dinámico sin validar - Confiar en que CORS protege contra todos los ataques: CORS es una restricción del navegador; no protege contra ataques server-to-server
- No manejar el preflight OPTIONS: los frameworks de IA suelen omitir este paso en el código generado
- Copiar la config de desarrollo a producción: frecuente cuando se usa IA para scaffolding rápido
3. Security Headers: La Primera Línea de Defensa del Navegador
¿Qué son los Security Headers?
Los Security Headers son cabeceras HTTP que el servidor envía junto con cada respuesta para instruir al navegador sobre cómo comportarse de forma segura. Protegen contra ataques como XSS (Cross-Site Scripting), clickjacking, inyección de contenido mixto y otros vectores comunes.
La IA raramente incluye security headers en el código que genera. No porque no los conozca, sino porque el desarrollador no los pide explícitamente. El resultado es que la mayoría de las aplicaciones construidas con asistencia de IA llegan a producción sin ninguna de estas protecciones activas.
Los Headers Esenciales y Qué Hacen
Content-Security-Policy (CSP)
Define exactamente qué recursos (scripts, estilos, imágenes, iframes) puede cargar el navegador. Es la defensa más poderosa contra ataques XSS.
Content-Security-Policy:
default-src 'self';
script-src 'self' https://cdn.jsdelivr.net;
style-src 'self' 'unsafe-inline' https://fonts.googleapis.com;
img-src 'self' data: https:;
font-src 'self' https://fonts.gstatic.com;
connect-src 'self' https://api.tudominio.com;
frame-ancestors 'none';
base-uri 'self';
form-action 'self';Strict-Transport-Security (HSTS)
Fuerza al navegador a usar siempre HTTPS, incluso si el usuario escribe http://. Protege contra ataques de downgrade y SSL stripping.
Strict-Transport-Security: max-age=31536000; includeSubDomains; preloadX-Frame-Options
Previene que tu sitio sea embebido en un iframe, bloqueando ataques de clickjacking.
X-Frame-Options: DENYX-Content-Type-Options
Evita que el navegador intente «adivinar» el tipo de contenido de una respuesta, previniendo ataques de MIME sniffing.
X-Content-Type-Options: nosniffReferrer-Policy
Controla cuánta información de la URL actual se comparte cuando el usuario navega a otro sitio.
Referrer-Policy: strict-origin-when-cross-originPermissions-Policy
Restringe el acceso del navegador a APIs sensibles como cámara, micrófono o geolocalización.
Permissions-Policy: camera=(), microphone=(), geolocation=(), payment=()Implementación Completa en Nginx
server {
listen 443 ssl;
server_name tudominio.com;
# Security Headers
add_header Content-Security-Policy "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; connect-src 'self'; frame-ancestors 'none';" always;
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;
add_header X-Frame-Options "DENY" always;
add_header X-Content-Type-Options "nosniff" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
add_header Permissions-Policy "camera=(), microphone=(), geolocation=()" always;
# Ocultar versión del servidor
server_tokens off;
}Implementación en Node.js con Helmet
import helmet from 'helmet';
app.use(helmet({
contentSecurityPolicy: {
directives: {
defaultSrc: ["'self'"],
scriptSrc: ["'self'"],
styleSrc: ["'self'", "'unsafe-inline'"],
imgSrc: ["'self'", "data:", "https:"],
connectSrc: ["'self'", "https://api.tudominio.com"],
frameAncestors: ["'none'"],
},
},
hsts: {
maxAge: 31536000,
includeSubDomains: true,
preload: true,
},
frameguard: { action: 'deny' },
noSniff: true,
referrerPolicy: { policy: 'strict-origin-when-cross-origin' },
}));Implementación en PHP / Laravel
// app/Http/Middleware/SecurityHeaders.php
namespace App\Http\Middleware;
use Closure;
class SecurityHeaders
{
public function handle($request, Closure $next)
{
$response = $next($request);
$response->headers->set(
'Content-Security-Policy',
"default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; frame-ancestors 'none';"
);
$response->headers->set('Strict-Transport-Security', 'max-age=31536000; includeSubDomains; preload');
$response->headers->set('X-Frame-Options', 'DENY');
$response->headers->set('X-Content-Type-Options', 'nosniff');
$response->headers->set('Referrer-Policy', 'strict-origin-when-cross-origin');
$response->headers->set('Permissions-Policy', 'camera=(), microphone=(), geolocation=()');
return $response;
}
}Cómo Verificar Tus Security Headers
Una vez que los implementes, podés verificar el estado de seguridad de tu sitio de forma gratuita en securityheaders.com. La herramienta analiza las cabeceras HTTP de tu dominio y asigna una calificación de A+ a F, con explicaciones detalladas de cada header.
Las 3 Medidas en Perspectiva: Capas de Seguridad
Estas tres medidas no son alternativas entre sí, sino capas complementarias que protegen distintos niveles de tu aplicación:
- RLS protege los datos en la capa de base de datos. Aunque todo lo demás falle, los datos siguen siendo inaccesibles para usuarios no autorizados.
- CORS protege tu API en la capa de comunicación. Controla qué orígenes pueden interactuar con tu backend desde el navegador del usuario.
- Security Headers protegen al usuario en la capa del navegador. Reducen el impacto de ataques XSS, clickjacking y exfiltración de datos en el cliente.
Ninguna de las tres es suficiente por sí sola. Aplicadas en conjunto, forman un modelo de defensa en profundidad que es la base de cualquier aplicación web seria.
Checklist de Seguridad para Aplicaciones con IA
Antes de llevar tu próxima aplicación generada con IA a producción, revisá estos puntos:
- ¿Está RLS habilitado en todas las tablas que contienen datos de usuarios?
- ¿Existen políticas de SELECT, INSERT, UPDATE y DELETE para cada tabla con RLS?
- ¿El CORS de producción tiene una lista blanca explícita de orígenes permitidos?
- ¿Hay un origen comodín (
*) activo en producción? - ¿El servidor envía Content-Security-Policy en todas las respuestas?
- ¿Está configurado HSTS con un
max-agede al menos un año? - ¿Los headers
X-Frame-OptionsyX-Content-Type-Optionsestán presentes? - ¿Analizaste la puntuación en securityheaders.com?
- ¿Revisaste manualmente el código de autenticación y autorización generado por la IA?
Las herramientas de IA transformaron la forma en que desarrollamos aplicaciones web. Lo que antes tomaba semanas hoy toma días, y lo que tomaba días puede resolverse en horas. Pero esa velocidad no viene sin costos: la seguridad es el área donde la IA más falla, no porque no sea capaz, sino porque el desarrollador no siempre hace las preguntas correctas.
Implementar Row Level Security, configurar CORS correctamente y agregar Security Headers no son tareas complejas. Representan quizás unas pocas horas de trabajo que pueden ahorrarte semanas de crisis ante una brecha de seguridad, pérdida de datos de usuarios o una demanda legal.











