DEV Community

Edgar (Homz) Macias
Edgar (Homz) Macias

Posted on

Modulo 4 - API Gateway

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...
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

😭 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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

Lambda + API Gateway:

Chef + Mesero + Seguridad
- El público puede pedir (HTTP)
- Mesero controla entrada (throttling)
- Registra quién pidió qué (logs)
Enter fullscreen mode Exit fullscreen mode

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 ...
Enter fullscreen mode Exit fullscreen mode

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"
}
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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"
  }
}
Enter fullscreen mode Exit fullscreen mode

Cómo Funciona

Burst Limit (50 simultáneos):

Llegan 100 requests al mismo tiempo:
- API Gateway acepta 50
- Rechaza 50 (error 429)
Enter fullscreen mode Exit fullscreen mode

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)
Enter fullscreen mode Exit fullscreen mode

Quota Diario:

Request 1-10,000:  ✅ OK
Request 10,001:    ❌ 429 (excede quota)

Día siguiente (00:00 UTC):
  Quota resetea
Enter fullscreen mode Exit fullscreen mode

🔨 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
Enter fullscreen mode Exit fullscreen mode

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
  }
}
Enter fullscreen mode Exit fullscreen mode

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"
}
Enter fullscreen mode Exit fullscreen mode

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
}
Enter fullscreen mode Exit fullscreen mode

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
}
Enter fullscreen mode Exit fullscreen mode

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"
}
Enter fullscreen mode Exit fullscreen mode

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"
}
Enter fullscreen mode Exit fullscreen mode

Deploy:

terraform apply

# Output:
api_gateway_url = "https://abc123.execute-api.us-east-1.amazonaws.com/dev/sessions"
Enter fullscreen mode Exit fullscreen mode

🧪 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"}'
Enter fullscreen mode Exit fullscreen mode

Respuesta:

{
  "session_id": "f7a3b2c1-4d5e-6789-abcd-ef0123456789",
  "user_id": "test",
  "expires_at": 1734393600,
  "message": "Session created successfully"
}
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

Resultado:

201  ← Request 1
201  ← Request 2
...
201  ← Request 100
429  ← Request 101 (Throttled!)
429  ← Request 102
...
429  ← Request 200
Enter fullscreen mode Exit fullscreen mode

Throttling funciona - Después de 100 req/seg, rechaza con 429.


Test 3: CORS

curl -X OPTIONS "$API_URL" -v
Enter fullscreen mode Exit fullscreen mode

Headers:

< access-control-allow-origin: *
< access-control-allow-methods: POST,OPTIONS
< access-control-allow-headers: Content-Type
Enter fullscreen mode Exit fullscreen mode

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)
Enter fullscreen mode Exit fullscreen mode

Desarrollo (3,000 req/mes):

API Gateway: $0.01
Lambda: $0.002
Total: $0.01/mes

Con Free Tier: $0.00
Enter fullscreen mode Exit fullscreen mode

🆘 Problemas Que Tuve (Y Cómo los Resolví)

Error 1: "Missing Authentication Token"

Síntoma:

{"message": "Missing Authentication Token"}
Enter fullscreen mode Exit fullscreen mode

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
#                                                     ^^^^^^^^
Enter fullscreen mode Exit fullscreen mode

Error 2: "Forbidden" (403)

Síntoma:

{"message": "Forbidden"}
Enter fullscreen mode Exit fullscreen mode

Causa: Lambda permission faltante.

Fix:

resource "aws_lambda_permission" "api_gateway_invoke" {
  # ← Este recurso estaba faltando
  principal = "apigateway.amazonaws.com"
}
Enter fullscreen mode Exit fullscreen mode

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
}
Enter fullscreen mode Exit fullscreen mode

💡 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"
}
Enter fullscreen mode Exit fullscreen mode

Cliente necesita:

curl -H 'x-api-key: abc123...'
Enter fullscreen mode Exit fullscreen mode

Custom Domain

En lugar de:

https://abc123.execute-api.us-east-1.amazonaws.com/dev/sessions
Enter fullscreen mode Exit fullscreen mode

Usar:

https://api.tudominio.com/sessions
Enter fullscreen mode Exit fullscreen mode

Requiere:

  • Route53 (DNS)
  • ACM (Certificado SSL)
  • API Gateway custom domain

📦 Código Completo

Todo el código está en GitHub:

GitHub logo 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


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)