Ciberseguridad · Gestión de riesgos · Modelado de amenazas · Backend
Arreglar mis propios hallazgos: un plan de remediación, clasificado por riesgo
Mi modelo de amenaza de este formulario de contacto del sitio encontró cuatro debilidades. No todos los hallazgos merecen la misma urgencia. Aquí está cómo los clasifiqué por probabilidad e impacto, cómo planeo solucionar cada uno y por qué estoy eligiendo aceptar uno de ellos.
En mi modelo de amenaza de este formulario de contacto del sitio, terminé con cuatro debilidades que había encontrado en mi propio diseño. Encontrar problemas es la parte fácil. La parte más difícil es decidir qué solucionar primero, cómo y qué no solucionar en absoluto.
Este artículo es esa segunda parte: un pequeño plan de remedio real.

Cómo los clasifiqué
Una forma sencilla de comparar riesgos es hacerse dos preguntas sobre cada uno:
- Probabilidad: ¿cuán probable es que esta debilidad sea explotada o cause daño?
- Impacto: si sucede, ¿qué tan malo es y para quién?
La matriz anterior es mi propio juicio, no una medición. Eso es normal: la mayoría de las evaluaciones de riesgos reales son juicios informados, escritos para que otros puedan cuestionarlos. El valor radica en hacer visible el razonamiento.
1. Mensajes guardados para siempre: solucionar primero
El riesgo. Cada mensaje enviado a través del formulario permanece en la base de datos de forma indefinida. Cada uno contiene un nombre, una dirección de correo electrónico y lo que la persona eligió decirme. Si la base de datos se expusiera alguna vez, a través de una credencial filtrada o un error mío, todo lo enviado se expondría con ella.
Por qué se clasifica primero. El impacto recae en otras personas, que confiaron en mí con sus detalles. Y la probabilidad de que algún almacenamiento de datos se exponga eventualmente a lo largo de muchos años no es pequeña. Los datos que no se guardan no pueden filtrarse.
La solución. Un trabajo programado que elimina los mensajes más antiguos que un período fijo. Los Cloudflare Workers admiten disparadores cron, así que el mismo Worker que recibe los mensajes puede ejecutar una consulta diaria DELETE FROM messages WHERE created_at < …. Doce meses parecen adecuados: lo suficientemente largos para seguir una conversación, lo suficientemente cortos para limitar lo que una violación podría revelar. Esto también acerca el sitio al principio de limitación de almacenamiento en la Ley de Protección de Datos de Ghana, 2012 (Ley 843).
2. 'unsafe-inline' en la política de script
El riesgo. La Política de Seguridad de Contenido del sitio permite scripts en línea. La CSP es una red de seguridad contra la ejecución de scripts entre sitios (XSS): si un atacante alguna vez lograra inyectar un script en una página, una política estricta lo detendría. Con 'unsafe-inline', la red tiene un agujero en el lugar exacto equivocado.
Por qué se clasifica segundo. La probabilidad es baja: el sitio es estático y no muestra contenido enviado por el usuario, por lo que no hay un punto de inyección obvio hoy en día. Pero el impacto de un XSS exitoso en cada visitante sería alto, y la defensa en profundidad significa no confiar en "no hay error hoy".
La solución. Reemplazar 'unsafe-inline' con un hash para cada script en línea. En el momento de la compilación, calcular el hash SHA-256 de cada script en línea y enumerar esos hashes en la política. El navegador ejecuta solo los scripts cuyos contenidos exactos yo aprobé. Debido a que el sitio es estático, los hashes solo cambian cuando cambio un script, por lo que esto se puede automatizar en la compilación.
3. Un límite de velocidad que se puede superar
El riesgo. El límite de cinco mensajes por hora por IP se almacena en Cloudflare KV, que es eventualmente coherente, y el código lee el contador y luego lo escribe como dos pasos separados. Una ráfaga de solicitudes enviadas en paralelo puede leer el mismo recuento antiguo y pasar ligeramente el límite.
Por qué ocupa el tercer lugar. Es fácil de activar, pero el impacto es pequeño: unos pocos mensajes de spam adicionales que aún tienen que pasar la verificación del bot Turnstile. Para un formulario de contacto, un límite aproximado es aceptable. No sería aceptable para algo como intentos de inicio de sesión.
La solución. Mover el contador a algo con actualizaciones atómicas: ya sea la limitación de velocidad integrada de Cloudflare para Workers, o un objeto duradero, que procesa las solicitudes para una clave determinada una a la vez. Cualquiera de ellos elimina la carrera.
4. Las IPs compartidas comparten un límite: aceptar, documentar, monitorear
El riesgo. Muchas redes móviles colocan un gran número de usuarios detrás de un pequeño grupo de direcciones IP públicas compartidas. En dicha red, un usuario abusivo podría agotar el límite horario para personas inocentes que comparten su dirección.
Por qué lo acepto. Toda alternativa tiene un costo. Limitar por dirección de correo electrónico es fácil de evadir con direcciones inventadas. Eliminar el límite de IP debilitaría la protección contra inundaciones. Aumentar el límite mucho dejaría pasar más spam. Dado que un visitante bloqueado puede intentarlo de nuevo más tarde o enviarme un correo electrónico directamente, el daño es pequeño.
Qué significa “aceptar”. Aceptar un riesgo es una decisión legítima, pero solo cuando es consciente y registrada. Así que lo estoy escribiendo aquí, con el razonamiento, y lo revisaré si alguna vez veo a visitantes reales siendo bloqueados. Si esto fuera un servicio para miles de usuarios móviles, en lugar de un formulario de contacto personal, lo clasificaría mucho más alto.
Lo que este ejercicio me enseñó
Dos lecciones:
- No todos los hallazgos son urgentes. Clasificar por probabilidad e impacto me impidió gastar mi tiempo en la solución técnica más interesante (la carrera de límite de velocidad) antes de la que realmente protege a las personas (eliminar datos antiguos).
- “Solucionar”, “mitigar” y “aceptar” son todos resultados válidos, siempre y cuando la elección sea deliberada. Un plan de seguridad que afirma solucionar todo generalmente es uno que no ha mirado lo suficiente.
Actualizaré esta publicación a medida que cada solución se implemente.
¿No estás de acuerdo con mi clasificación? Me gustaría saber por qué.
