miércoles, 25 de febrero de 2026

Cómo evitar ataques CSRF en Google Apps Script API

El CSRF (Cross-Site Request Forgery) es uno de los ataques más comunes contra APIs públicas mal protegidas.

Si usas Google Apps Script como backend, este artículo te muestra cómo proteger tu API con un token CSRF real, simple y efectivo.

Sin teoría innecesaria. Código directo.


Qué es un ataque CSRF (rápido)

Un atacante:

  • Aprovecha que tu API no valida el origen
  • Envía requests POST externos
  • Ejecuta acciones críticas sin autorización

Resultado:

  • Registros falsos
  • Logins forzados
  • Abuso de recursos
  • Automatización vía curl

El problema real en Apps Script

Por defecto:

  • doPost(e) acepta cualquier POST
  • No hay sesiones reales
  • No hay headers custom confiables
  • Muchas APIs quedan totalmente abiertas

Ejemplo real de API vulnerable:

curl -X POST "https://script.google.com/macros/s/XXXX/exec" \  
-d "op=register" \  
-d "email=test@example.com" \  
-d "password=12345"

Si esto funciona sin validación → estás expuesto.

Estrategia correcta contra CSRF

Lo mínimo viable y efectivo:

  • Token único (UUID)
  • Expiración corta
  • Uso único (anti-replay)
  • Almacenado en PropertiesService

Flujo seguro

  1. Cliente solicita token CSRF
  2. Backend genera token temporal
  3. Cliente envía token en POST
  4. Backend valida:
    • existe
    • no expiró
    • no fue usado
  5. Token se elimina

Implementación en Google Apps Script

Generar token CSRF (GET)

function generateCSRFToken() {
  const token = Utilities.getUuid();
  const expira = Date.now() + (5 * 60 * 1000); // 5 minutos

  PropertiesService.getScriptProperties().setProperty(
    token,
    expira.toString()
  );

  return token;
}

Exponer token vía GET

function doGet(e = {}) {
  const { op } = e.parameter || {};

  if (op === "csrf") {
    return returnJSON(generateCSRFToken());
  }

  return returnJSON("hello world testing");
}

Validación CSRF en POST (núcleo del sistema)

function readParamPOST(e = {}) {
  const data = e.parameter;
  const token = data.csrf;

  const store = PropertiesService.getScriptProperties();
  const expira = store.getProperty(token);

  if (!expira) {
    return { status: "error", message: "CSRF inválido" };
  }

  if (Date.now() > Number(expira)) {
    store.deleteProperty(token);
    return { status: "error", message: "CSRF expirado" };
  }

  // Anti-replay
  store.deleteProperty(token);

  if (!data.op) {
    return { status: "error", message: "Operación no válida" };
  }

  if (data.op === "login") {
    // login(data)
  }

  if (data.op === "register") {
    // register(data)
  }

  return { status: "ok" };
}

Handler POST completo

function doPost(e = {}) {
  return returnJSON(readParamPOST(e));
}

Qué ataques bloquea este enfoque

  • CSRF automatizado
  • Replays
  • Bots simples
  • Abuso con curl
  • Formularios externos

No es perfecto, pero sube brutalmente la seguridad.


Conclusión

Si usas Google Apps Script como API:

  • Sin CSRF = API abierta
  • Con este patrón:
    • Seguridad real
    • Código simple
    • Sin librerías externas

Ideal para:

  • MVPs
  • Side projects
  • SaaS pequeños
  • Automatizaciones

No hay comentarios:

Publicar un comentario