Cómo Protegí Mi API de un Ataque de $5,904 (Con Terraform)
📚 Serie: AWS Zero to Architect - Módulo 4
⏱️ Tiempo de lectura: 18 minutos
💻 Tiempo de implementación: 90 minutos
En los módulos anteriores construí una Lambda en Go que funciona perfectamente. Pero tenía un problema: solo yo podía invocarla (con AWS CLI).
Ahora voy a hacerla pública... pero sin que me hackeen la tarjeta de crédito. 💳
😱 El Horror de las Facturas Inesperadas
El Escenario Pesadilla
Imagina esto:
Lunes 9:00 AM: Despliegas tu API pública
Lunes 9:05 AM: Un bot la descubre
Lunes 9:10 AM: Envía 1,000,000 requests/minuto
Martes 9:00 AM: Email de AWS...
Email:
Your AWS Bill: $5,904.00
API Gateway: $3.50/min × 1,440 min = $5,040
Lambda: $0.60/min × 1,440 min = $864
😭 Un día de ataque = tu salario del mes.
La Solución: Throttling
Con throttling configurado:
Máximo: 100 requests/segundo
Quota: 10,000 requests/día
Bot envía 1M requests/min
→ API Gateway rechaza el exceso
→ Solo pasan 6,000 requests/min
→ Máximo al día: 10,000 requests
Factura máxima: $0.041/día = $1.23/mes
✅ Imposible generar facturas de miles de dólares.
🤔 ¿Qué es API Gateway?
Analogía Simple
Lambda sola (Módulo 3):
Chef en cocina SIN puerta
- Solo el dueño entra (AWS CLI)
- Nadie puede pedir comida desde afuera
Lambda + API Gateway:
Chef + Mesero + Seguridad
- El público puede pedir (HTTP)
- Mesero controla entrada (throttling)
- Registra quién pidió qué (logs)
Lo Que NO Tenía (Módulo 3)
# ❌ Esto NO funcionaba
curl https://mi-api.com/sessions
# ✅ Solo esto (requiere credenciales AWS)
aws lambda invoke --function-name mi-lambda ...
Lo Que SÍ Tengo Ahora
# ✅ Esto funciona desde CUALQUIER lugar
curl -X POST https://abc123.execute-api.us-east-1.amazonaws.com/dev/sessions \
-H 'Content-Type: application/json' \
-d '{"user_id": "test"}'
# Respuesta (201 Created):
{
"session_id": "f7a3b2c1-...",
"user_id": "test",
"expires_at": 1734393600,
"message": "Session created successfully"
}
Sin credenciales AWS. Sin nada. Solo un curl. 🚀
💰 Pricing Honesto (Sin Sorpresas)
Costos por Uso
API Gateway REST API:
$3.50 por millón de requests
Lambda (Go ARM64):
$0.60 por millón de invocaciones
Ejemplos reales:
10,000 requests/mes:
API Gateway: $0.035
Lambda: $0.006
Total: $0.041/mes
100,000 requests/mes:
API Gateway: $0.35
Lambda: $0.06
Total: $0.41/mes
Free Tier (primeros 12 meses):
- 1 millón de requests gratis/mes
Mi Costo Real
Desarrollo: ~100 requests/día
= 3,000 requests/mes
= $0.01/mes
Con Free Tier: $0.00
Estoy gastando CERO dólares. 💸
Máximo Posible (Con Quota)
Quota: 10,000 requests/día
= 300,000 requests/mes
= $1.05 (API Gateway) + $0.18 (Lambda)
= $1.23/mes
Máximo absoluto: $1.23/mes
Es IMPOSIBLE que me cueste más. ✅
🛡️ Throttling Explicado
¿Qué es Throttling?
Rate Limiting = Límite de requests por segundo.
Configuración
resource "aws_api_gateway_method_settings" "main" {
settings {
throttling_burst_limit = 50 # Simultáneos
throttling_rate_limit = 100 # Por segundo
}
}
resource "aws_api_gateway_usage_plan" "main" {
quota_settings {
limit = 10000 # Por día
period = "DAY"
}
}
Cómo Funciona
Burst Limit (50 simultáneos):
Llegan 100 requests al mismo tiempo:
- API Gateway acepta 50
- Rechaza 50 (error 429)
Rate Limit (100/segundo):
Segundo 1:
Request 1-100: ✅ OK
Request 101: ❌ 429 Too Many Requests
Request 102+: ❌ 429
Segundo 2:
Request 101-200: ✅ OK (nuevo segundo)
Quota Diario:
Request 1-10,000: ✅ OK
Request 10,001: ❌ 429 (excede quota)
Día siguiente (00:00 UTC):
Quota resetea
🔨 Implementación con Terraform
Arquitectura Completa
Internet
↓
API Gateway
├─ Throttling: 100 req/seg
├─ Quota: 10k req/día
└─ CORS enabled
↓
Lambda (Go ARM64)
└─ Create session
↓
DynamoDB
└─ Store session
Código Clave
1. REST API
resource "aws_api_gateway_rest_api" "main" {
name = "go-hexagonal-auth-dev-api"
endpoint_configuration {
types = ["REGIONAL"] # Más barato
}
}
2. Resource (Path)
resource "aws_api_gateway_resource" "sessions" {
rest_api_id = aws_api_gateway_rest_api.main.id
parent_id = aws_api_gateway_rest_api.main.root_resource_id
path_part = "sessions"
}
3. Method POST
resource "aws_api_gateway_method" "create_session" {
rest_api_id = aws_api_gateway_rest_api.main.id
resource_id = aws_api_gateway_resource.sessions.id
http_method = "POST"
authorization = "NONE" # Público
}
4. Integration (Lambda)
resource "aws_api_gateway_integration" "lambda_integration" {
type = "AWS_PROXY" # Pasa todo a Lambda
uri = aws_lambda_function.create_session.invoke_arn
}
5. Lambda Permission
resource "aws_lambda_permission" "api_gateway_invoke" {
statement_id = "AllowAPIGatewayInvoke"
action = "lambda:InvokeFunction"
function_name = aws_lambda_function.create_session.function_name
principal = "apigateway.amazonaws.com"
}
6. Deployment + Stage
resource "aws_api_gateway_deployment" "main" {
rest_api_id = aws_api_gateway_rest_api.main.id
}
resource "aws_api_gateway_stage" "main" {
deployment_id = aws_api_gateway_deployment.main.id
stage_name = "dev"
}
Deploy:
terraform apply
# Output:
api_gateway_url = "https://abc123.execute-api.us-east-1.amazonaws.com/dev/sessions"
🧪 Testing Real
Test 1: Request Básico
API_URL="https://abc123.execute-api.us-east-1.amazonaws.com/dev/sessions"
curl -X POST "$API_URL" \
-H 'Content-Type: application/json' \
-d '{"user_id": "test"}'
Respuesta:
{
"session_id": "f7a3b2c1-4d5e-6789-abcd-ef0123456789",
"user_id": "test",
"expires_at": 1734393600,
"message": "Session created successfully"
}
✅ StatusCode: 201
Test 2: Throttling (El Momento de Verdad)
# Enviar 200 requests rápidamente
for i in {1..200}; do
curl -X POST "$API_URL" \
-H 'Content-Type: application/json' \
-d "{\"user_id\": \"load-$i\"}" \
-s -o /dev/null -w "%{http_code}\n" &
done
Resultado:
201 ← Request 1
201 ← Request 2
...
201 ← Request 100
429 ← Request 101 (Throttled!)
429 ← Request 102
...
429 ← Request 200
✅ Throttling funciona - Después de 100 req/seg, rechaza con 429.
Test 3: CORS
curl -X OPTIONS "$API_URL" -v
Headers:
< access-control-allow-origin: *
< access-control-allow-methods: POST,OPTIONS
< access-control-allow-headers: Content-Type
✅ Frontend puede llamar la API desde browsers.
🎯 Resultados Reales
Performance
Primera invocación (cold start):
- API Gateway: ~20ms
- Lambda Go: ~156ms
- DynamoDB: ~30ms
- Total: ~206ms
Warm invocations:
- Total: ~95ms
Costos
Testing (100 requests):
API Gateway: 100 × $0.0000035 = $0.00035
Lambda: 100 × $0.0000006 = $0.00006
Total: $0.0004 (0.04 centavos)
Desarrollo (3,000 req/mes):
API Gateway: $0.01
Lambda: $0.002
Total: $0.01/mes
Con Free Tier: $0.00
🆘 Problemas Que Tuve (Y Cómo los Resolví)
Error 1: "Missing Authentication Token"
Síntoma:
{"message": "Missing Authentication Token"}
Causa: URL incorrecta.
Fix:
# Incorrecto:
https://abc.execute-api.us-east-1.amazonaws.com/dev
# Correcto:
https://abc.execute-api.us-east-1.amazonaws.com/dev/sessions
# ^^^^^^^^
Error 2: "Forbidden" (403)
Síntoma:
{"message": "Forbidden"}
Causa: Lambda permission faltante.
Fix:
resource "aws_lambda_permission" "api_gateway_invoke" {
# ← Este recurso estaba faltando
principal = "apigateway.amazonaws.com"
}
Error 3: CORS No Funciona
Síntoma: Browser bloquea el request.
Causa: Falta método OPTIONS.
Fix:
resource "aws_api_gateway_method" "options_sessions" {
http_method = "OPTIONS" # ← Necesario para CORS
}
💡 Lo Que Aprendí
1. Throttling es Obligatorio
Sin throttling:
- Un bot puede generar facturas de miles de dólares
Con throttling:
- Costo máximo predecible ($1.23/mes)
2. AWS_PROXY es Más Flexible
AWS_PROXY:
- Lambda recibe todo el evento
- Lambda retorna respuesta completa
- Más control
Custom Integration:
- API Gateway transforma request/response
- Más configuración
- Menos flexible
3. CORS Requiere OPTIONS
Para que browsers funcionen:
- Método OPTIONS (preflight)
- Headers Access-Control-Allow-*
- Integration MOCK (no llama Lambda)
4. Regional > Edge (Para Desarrollo)
REGIONAL:
- $3.50 por millón
- Sin CDN
- Suficiente para APIs regionales
EDGE:
- $3.50 + costos de CloudFront
- CDN global
- Para apps globales
📊 Comparación de Costos
| Servicio | Requests/mes | Costo |
|---|---|---|
| Heroku Dyno | Ilimitado | $7/mes |
| AWS EC2 t3.micro | Ilimitado | $7.50/mes |
| API Gateway + Lambda | 10,000 | $0.04/mes |
| API Gateway + Lambda | 100,000 | $0.41/mes |
| API Gateway + Lambda | 1,000,000 | $4.10/mes |
Ganador: Serverless (para tráfico variable)
🎓 Lo Que Lograste
Si llegaste hasta aquí e implementaste todo:
- ✅ Endpoint HTTPS público
- ✅ Throttling (100 req/seg)
- ✅ Quota (10k req/día)
- ✅ CORS habilitado
- ✅ Logs en CloudWatch
- ✅ Costo controlado ($1.23/mes máximo)
- ✅ Lambda protegida contra abuso
Y todo por menos de $0.50/mes. 💪
🚀 Próximos Pasos
Agregar Autenticación
API Keys:
resource "aws_api_gateway_api_key" "main" {
name = "production-key"
}
Cliente necesita:
curl -H 'x-api-key: abc123...'
Custom Domain
En lugar de:
https://abc123.execute-api.us-east-1.amazonaws.com/dev/sessions
Usar:
https://api.tudominio.com/sessions
Requiere:
- Route53 (DNS)
- ACM (Certificado SSL)
- API Gateway custom domain
📦 Código Completo
Todo el código está en GitHub:
edgar-macias-se
/
go-hexagonal-auth
Production-ready Authentication Microservice in Go. Implements Hexagonal Architecture, JWT, Redis Blacklisting, and Rate Limiting.
🛡️ Go Secure Authentication Service
Un microservicio de autenticación robusto, escalable y listo para producción, escrito en Go siguiendo principios de Arquitectura Hexagonal.
Diseñado con la seguridad como prioridad, implementando las mejores prácticas de OWASP para la gestión de identidad, sesiones y protección contra ataques.
🚀 Key Security Highlights
Este no es solo un login básico. Este proyecto implementa capas de defensa en profundidad:
- 🔒 Arquitectura Hexagonal (Ports & Adapters): Desacoplamiento total entre la lógica de negocio, la base de datos y la API HTTP. Código testable y mantenible.
-
🔑 Estrategia de Tokens Duales (JWT):
- Access Token (15 min): JWT firmado (HS256) de vida corta para minimizar riesgos en caso de robo.
- Refresh Token (7 días): Token opaco rotativo almacenado en BD para renovar sesiones sin exponer credenciales.
- 🛡️ Protección contra Fuerza Bruta (Rate Limiting): Middleware distribuido usando Redis. Bloquea IPs/Usuarios tras 5 intentos fallidos por 15 minutos.
- …
Carpetas:
-
terraform/api_gateway.tf- API Gateway config -
terraform/lambda.tf- Lambda config -
cmd/lambda/- Handler en Go -
internal/- Domain + Adapters
💬 Tu Turno
¿Has tenido facturas inesperadas en AWS? Cuéntame en los comentarios cómo las resolviste.
¿Prefieres serverless o servidores tradicionales? ¿Por qué?
¿Qué otros casos de uso ves para API Gateway? Me gustaría saber tus ideas.
🔗 Conecta
- GitHub: @edgar-macias-se
- LinkedIn: edgar-macias-devcybsec
- Website: edgarmacias.com/es
- Dev.to: @emp_devcybsec
Serie: AWS Zero to Architect
Anterior: Módulo 3 - Lambda con Go
Siguiente: Módulo 5 - Autenticación JWT (próximamente)
💡 Tip: Si este tutorial te salvó de una factura de $5,904, compártelo con tus colegas que están empezando con serverless.
Top comments (0)