---
title: "Seguridad AI-Ready: Rate Limiting, validación y anti-spam"
description: "Los endpoints de API públicos son puerta para spam. Cómo Zod, rate limiting y sanitización de errores aseguran sitios AI-Ready desde el día uno."
author: "Matthias Meyer"
published: 2026-02-15
updated: 2026-02-15
language: es
tags: ["security", "rate-limiting", "zod", "validation", "api", "ai-ready"]
canonical: "https://studiomeyer.io/es/blog/ai-ready-security"
markdown_versions: ["https://studiomeyer.io/de/blog/ai-ready-security.md", "https://studiomeyer.io/en/blog/ai-ready-security.md", "https://studiomeyer.io/es/blog/ai-ready-security.md"]
publisher: "StudioMeyer, https://studiomeyer.io (llms.txt: https://studiomeyer.io/llms.txt)"
---

# Seguridad AI-Ready: Rate Limiting, validación y anti-spam

Los sitios web AI-Ready tienen endpoints de API publicos. Ese es todo el punto -- los agentes de IA deben poder usar servicios sin intervencion humana. Pero endpoints publicos tambien significa: cualquiera puede llamarlos. No solo agentes de IA utiles, sino tambien bots, scrapers y atacantes.

Quien construye un sitio web AI-Ready y no piensa en la seguridad desde el dia uno, esta construyendo una puerta abierta.

## El problema: Publico significa atacable

Un sitio web clasico tiene un formulario de contacto. Proteccion contra spam? reCAPTCHA, campos honeypot, quizas un rate limit. Eso funciona porque los humanos rellenan formularios -- y la mayoria de los bots no pueden resolver CAPTCHAs.

Un sitio web AI-Ready tiene endpoints de API como `/api/v1/quote` o `/api/v1/consultation`. Estos endpoints estan *disenados* para uso automatizado. Un CAPTCHA destruiria todo el proposito -- los agentes de IA deben acceder programaticamente a ellos.

Esto crea un dilema: **La puerta debe estar abierta para agentes legitimos, pero cerrada para el abuso.** La solucion no es un unico mecanismo, sino multiples capas.

## Capa 1: Validacion de entrada con Zod

La primera linea de defensa es la mas simple: verificar si los datos entrantes son validos *antes* de que activen cualquier cosa.

Zod es una biblioteca de validacion para TypeScript que verifica datos contra un esquema definido. Sin magia, sin IA -- pura validacion estructural.

### Por que no simplemente sentencias `if`?

Se podria. Pero con estructuras de datos complejas, esto rapidamente se vuelve inmanejable y propenso a errores. Zod impone una estructura clara:

```typescript
import { z } from "zod";

const quoteSchema = z.object({
  projectType: z.enum(["website", "webshop", "webapp", "redesign"]),
  pages: z.string().optional(),
  features: z.string().optional(),
});

// En la ruta de la API:
const parsed = quoteSchema.safeParse(body);
if (!parsed.success) {
  return ApiError.validation(
    "Datos de entrada invalidos",
    parsed.error.issues
  );
}
```

### Que previene Zod concretamente

- **Campos obligatorios faltantes:** Una solicitud sin `projectType` se rechaza inmediatamente
- **Tipos incorrectos:** Un valor de `pages` como `true` en lugar de un string se detecta
- **Valores inesperados:** Un `projectType` de `"hacking"` se bloquea por el enum
- **Intentos de inyeccion:** La inyeccion SQL o NoSQL a traves de campos manipulados se dificulta por la tipificacion estricta

Suena basico -- y lo es. Pero la mayoria de las vulnerabilidades de seguridad no surgen de ataques sofisticados, sino de la falta de validacion de entrada.

### Practica: Validar una solicitud de consulta

```typescript
const consultationSchema = z.object({
  name: z.string().min(2).max(100),
  email: z.string().email(),
  phone: z.string().optional(),
  topic: z.string().min(5).max(500),
});
```

Esta validacion de esquema asegura:
- **Nombre** tiene al menos 2 y maximo 100 caracteres (sin cadenas vacias, sin entradas de longitud novelistica)
- **Email** es un formato de email valido (sin inyeccion de texto libre)
- **Tema** tiene 5-500 caracteres (sin envio vacio, sin payload de 10MB)

## Capa 2: Rate Limiting

La validacion protege contra datos incorrectos. El rate limiting protege contra demasiadas solicitudes.

### Como funciona el Rate Limiting

El principio es simple: contar cuantas solicitudes hace una direccion IP dentro de una ventana de tiempo. Si supera el limite, recibe un estado `429 Too Many Requests`.

```
IP 192.168.1.1:
  Ventana: 60 segundos
  Limite: 60 solicitudes
  Actual: 47
  → Permitido

IP 10.0.0.5:
  Ventana: 60 segundos
  Limite: 60 solicitudes
  Actual: 61
  → Bloqueado (Retry-After: 23s)
```

### Diferentes limites para diferentes endpoints

No todos los endpoints necesitan el mismo limite:

| Tipo de endpoint | Limite | Razon |
|-----------------|--------|-------|
| Endpoints GET (Portfolio, Services) | 60/minuto por IP | Leer es barato, mayor tolerancia |
| Endpoints POST (Quote, Consultation) | 10/minuto por IP | Escribir es costoso, control mas estricto |
| Endpoints de llamadas externas (AI-Ready Check) | 10/minuto por IP | Hacen llamadas HTTP externas, alto riesgo de abuso |

Por que la diferencia? Un endpoint GET lee datos de la base de datos -- eso es rapido y barato. Un endpoint POST escribe datos, potencialmente envia emails o dispara otras acciones. Y un endpoint que llama a sitios web externos (como nuestro AI-Ready Check) podria ser abusado para ataques DDoS contra terceros.

### La respuesta en Rate Limiting

Cuando se alcanza un limite, el cliente recibe una respuesta clara:

```json
{
  "error": "Rate limit exceeded. Max 60 requests per minute.",
  "retryAfterMs": 23000
}
```

Ademas de headers HTTP que le dicen al cliente (o agente) donde esta:

```
HTTP/1.1 429 Too Many Requests
Retry-After: 23
X-RateLimit-Limit: 60
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1739654400
```

Un agente de IA bien construido lee estos headers y espera automaticamente. Un atacante al menos recibe un freno.

## Capa 3: Sanitizacion de errores

Cuando algo sale mal -- y siempre sale mal eventualmente -- el mensaje de error no debe revelar detalles internos.

### Que aprende un atacante de los mensajes de error

Un error sin sanitizar podria verse asi:

```json
{
  "error": "PrismaClientKnownRequestError: Invalid `prisma.client.create()` invocation in /home/user/app/lib/db.ts:47:3",
  "stack": "at Object.create (/home/user/node_modules/@prisma/client/runtime/library.js:123:45)"
}
```

Que revela esto? El ORM (Prisma), la ruta del archivo, la linea en el codigo, la version de Node.js. Un atacante ahora sabe donde buscar.

### Como devolvemos errores

Nuestra API **siempre** devuelve el mismo formato:

```json
{
  "success": false,
  "error": {
    "code": "INTERNAL_ERROR",
    "message": "Internal server error"
  }
}
```

En modo desarrollo, los desarrolladores ven el mensaje de error completo. En produccion, cada llamante ve solo `"Internal server error"`. Sin stack trace, sin ruta de archivo, sin nombre de ORM.

### Los codigos de error

Usamos seis codigos de error definidos:

| Codigo | Estado HTTP | Significado |
|--------|-------------|-------------|
| `UNAUTHORIZED` | 401 | API key faltante o invalida |
| `FORBIDDEN` | 403 | La key no tiene el permiso necesario |
| `RATE_LIMITED` | 429 | Demasiadas solicitudes |
| `VALIDATION_ERROR` | 400 | Datos de entrada invalidos |
| `NOT_FOUND` | 404 | Recurso no encontrado |
| `INTERNAL_ERROR` | 500 | Error del servidor (sin detalles) |

Un agente de IA puede leer estos codigos y reaccionar: esperar en 429, corregir la solicitud en 400, autenticarse en 401.

## Capa 4: Configuracion CORS

CORS (Cross-Origin Resource Sharing) controla quien puede acceder a la API y desde donde.

Para endpoints de discovery AI-Ready (como `agents.json`), CORS debe estar abierto -- los agentes de IA vienen de todas partes. Pero hay matices:

```
Endpoints de Discovery (solo GET):
  Access-Control-Allow-Origin: *
  Access-Control-Allow-Methods: GET
  → Cualquiera puede leer, pero solo leer.

Endpoints de API:
  Access-Control-Allow-Methods: GET, POST, OPTIONS
  Access-Control-Allow-Headers: Content-Type, Authorization
  → Acceso completo, pero solo con headers permitidos.
```

El metodo OPTIONS es para solicitudes preflight -- los navegadores primero preguntan "Puedo?" antes de enviar la solicitud real. Cacheamos esta respuesta preflight por 24 horas (`Access-Control-Max-Age: 86400`) para evitar solicitudes innecesarias.

## Capa 5: Lo que no se expone

Igual de importante que asegurar los endpoints publicos es la pregunta: **Que permanece privado?**

En nuestro caso, eso incluye:
- **Herramientas MCP internas** (242 herramientas para desarrollo web -- no publicas)
- **IDs y estructuras de base de datos** (sin IDs de Prisma en las respuestas de la API)
- **Tokens de autenticacion** (nunca en logs ni en respuestas)
- **Configuracion del servidor** (sin numeros de version, sin rutas)
- **Datos personales de usuarios** (RGPD de todos modos, pero tambien aislados tecnicamente en la API)

Suena obvio, pero no lo es. Muchas APIs devuelven IDs de base de datos (`"id": 47`), de los que se puede deducir el numero total de registros. O registran stack traces en logs que son legibles publicamente a traves de herramientas de error-tracking.

## Conceptos de autenticacion: Hoy y manana

### Lo que tenemos hoy

Para los endpoints publicos de negocio (Portfolio, Services, Quote), deliberadamente no hay autenticacion. La razon: estos endpoints entregan informacion que tambien es visible publicamente en el sitio web. Una calculadora de precios no es un secreto.

Para endpoints avanzados (API de Animaciones, Analytics), usamos API keys con permisos con alcance definido:

```
Bearer sk_live_abc123...

Scopes: animations:read, animations:generate
Rate Limit: 100/min (configurable por key)
```

### Lo que trae el futuro

A medida que A2A y protocolos similares ganen adopcion mas amplia, la cuestion de la autenticacion de agentes se volvera central. Como demuestra un agente de IA que actua en nombre de un usuario especifico?

Posibles enfoques:
- **OAuth 2.0 Token Relay:** El agente recibe un token del usuario y lo reenvie
- **Certificados de identidad de agente:** Certificados digitales para agentes verificados
- **DID (Identificadores Descentralizados):** Identidades auto-soberanas para agentes

A dia de hoy, ninguno de estos enfoques esta estandarizado para comunicacion entre agentes. Pero los fundamentos (OAuth, certificados) existen -- lo que falta es la estandarizacion en el contexto A2A.

## La regla de oro: La seguridad no es una funcionalidad, es un prerrequisito

El error mas comun con sitios web AI-Ready: construir primero los endpoints, asegurarlos "despues". En nuestra experiencia, "despues" nunca llega -- o llega solo despues del primer incidente.

### Nuestra checklist para cada nuevo endpoint

```
[ ] Esquema Zod definido?
[ ] Rate limit configurado (GET: 60/min, POST: 10/min)?
[ ] Respuestas de error sanitizadas (sin stack traces)?
[ ] Headers CORS configurados?
[ ] Sin IDs de base de datos en la respuesta?
[ ] Sin datos personales sin autenticacion?
[ ] Logging sin datos sensibles?
```

Esta checklist no es sobrecarga. Es la diferencia entre un endpoint que es util y uno que se convierte en un problema.

## Resumen

| Capa | Protege contra | Mecanismo |
|------|---------------|-----------|
| **Validacion de entrada** | Datos incorrectos/manipulados | Esquemas Zod con tipos estrictos |
| **Rate Limiting** | Sobrecarga y fuerza bruta | Contadores por IP con ventanas de tiempo |
| **Sanitizacion de errores** | Fuga de informacion | Mensajes de error genericos en produccion |
| **CORS** | Acceso cross-origin no autorizado | Control de acceso basado en headers |
| **Auth (API Keys)** | Acceso no autorizado a endpoints premium | Bearer tokens con permisos con alcance |
| **No exposicion** | Divulgacion accidental de datos | Decisiones conscientes sobre que permanece privado |

La seguridad para sitios web AI-Ready no es un tema especial. Sigue los mismos principios fundamentales que cualquier API -- validacion, rate limiting, manejo limpio de errores. La diferencia es que los endpoints estan *disenados* para acceso automatizado, lo que hace la superficie de ataque mas grande desde el dia uno.

Quien piensa en esto desde el principio no tiene problemas. Quien lo anade despues tiene trabajo por delante.

## Seguir leyendo, Sitios AI-Ready

- [Añade .md a cualquiera de nuestras URLs del blog. Por qué importa](https://studiomeyer.io/es/blog/agent-readable-blog.md)
- [AI-Ready es universal. El hosting es regional.](https://studiomeyer.io/es/blog/ai-ready-hosting-regionen.md)
- [Diseño Web AI-Ready 2026: Cuando tu web está construida para humanos Y máquinas](https://studiomeyer.io/es/blog/ai-ready-webdesign-2026.md)
- [AI-Ready vs. sitio web clásico: Lo que realmente cambia](https://studiomeyer.io/es/blog/ai-ready-vs-klassisch.md)
