Rexvalentia

La Raya · backend (8): feedback sin cuentas — un formulario de contacto que no guarda datos

3 min de lectura

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.

Esc

Pulsa Intro para buscar en todo el blog.