La Raya · backend (8): feedback sin cuentas — un formulario de contacto que no guarda datos
Publicado el 21/06/2026
La Raya no tiene (ni quiere de momento) cuentas de usuario. Pero necesitaba una vía de feedback: "este horario está mal", "falta tal bar", "la app peta al abrir noticias". El reto era hacerlo sin pedir datos personales y sin abrir la puerta a que alguien me inunde el buzón.
El principio: si no lo recojo, no tengo que protegerlo
La decisión de partida fue de minimización (y de tranquilidad con el RGPD): no almacenar nada del usuario. Nada de email, nada de nombre. El formulario solo manda:
- un tema de una lista cerrada (una opción por sección de la app —Eventos, Gastronomía, Noticias, Turismo— más "Problema técnico" y "Otros"), y
- un mensaje de texto libre.
Si el usuario no da su email, no puedo responderle; lo asumí a propósito. "Ellos envían, yo intento resolver o no". Menos datos, menos problemas.
El problema interesante: limitar abuso sin identificar a nadie
Sin login, ¿cómo evito que alguien envíe 10.000 mensajes y me llene la base de datos o el correo? Necesitaba identificar el dispositivo para poner un límite, pero sin invadir la privacidad. Las opciones:
- Solo por IP: problemático. Las redes móviles y el NAT hacen que muchísima gente comparta IP → bloquearías a usuarios legítimos del mismo operador. Y es rotable cambiando de red.
- Solo un identificador de dispositivo: la app genera un UUID y lo manda. Cómodo, pero rotable (reinstalas y a empezar) → no frena a un atacante decidido.
La solución fue combinar ambos con un matiz de privacidad: la app genera un UUID persistente y lo envía en una cabecera; el límite principal (5 mensajes/día) se cuenta por ese dispositivo, en base de datos (autoritativo). Y como respaldo anti-DoS, un tope por red pero guardando solo la IP truncada a /24 (los tres primeros octetos, sin el último). Una /24 es mucho menos identificativa que una IP completa y basta como "unidad de abuso". Además, un throttle de ráfaga barato (en Redis) corta avalanchas antes de tocar la BD.
Así: el dispositivo es rotable, pero la /24 hace de red de seguridad; y la /24 sola no identifica a una persona. Equilibrio razonable entre frenar el abuso y no espiar.
Lo que llega al otro lado
Cada mensaje válido se reenvía por email al administrador (con el SMTP que ya configuré en el capítulo de despliegue) y queda guardado en el admin de Django por si el email falla — el mensaje nunca se pierde. El envío es síncrono y best-effort: si el correo falla, se registra el aviso pero la respuesta al usuario es la misma.
Un detalle: el endpoint es público (sin auth ni CSRF, porque no hay sesión) y la cabecera del dispositivo va en la lista blanca de CORS para que la app pueda mandarla.
Lo que me llevo
- La mejor protección de datos es no tenerlos. Si no recoges email ni nombre, no hay nada que filtrar, exportar ni que te reclamen. La minimización simplifica el diseño y la conciencia.
- Identificar un dispositivo no es identificar a una persona — si lo haces con cuidado. Un UUID anónimo + una /24 (sin el último octeto) limitan el abuso sin construir un perfil de nadie.
- Defensa en capas. Límite por dispositivo en BD (la regla de negocio) + tope por red + throttle de ráfaga (el anti-DoS). Cada capa cubre el agujero de la anterior.
- Que el dato sobreviva al email. Guardar el mensaje en el admin además de mandarlo evita perder feedback cuando el SMTP tiene un mal día.
Y con esto se cierra la serie del backend de La Raya: de una web de fichas a un agregador multilingüe, desplegado, con una sección de gastronomía de cuatro fuentes y un canal de feedback que no guarda datos. La otra mitad de la historia —la app móvil— tiene su propia serie, que empieza en la siguiente entrada.