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
- Cliente solicita token CSRF
- Backend genera token temporal
- Cliente envía token en POST
- Backend valida:
- existe
- no expiró
- no fue usado
- 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