Guía de seguridad

Este documento cubre la arquitectura de seguridad, los riesgos conocidos, las medidas de refuerzo aplicadas, las compensaciones aceptadas y los procedimientos de prueba de penetración para la plataforma de financiación colectiva The Pool.

Arquitectura de seguridad

Mecanismos de autenticación

Mecanismo Puntos finales Descripción
Fichas de enlace mágico /pledge*, /pledges, /votes Tokens firmados HMAC-SHA256 con vencimiento de 90 días
Tokens de cancelación de suscripción de recordatorio de lanzamiento GET /launch-reminders/unsubscribe Token HMAC con alcance que suprime un registro de recordatorio de campaña/correo electrónico
Firma de webhook de rayas /webhooks/stripe Verificación HMAC-SHA256 según las especificaciones de Stripe
Sesiones del panel de administración API del panel del navegador /admin/* Inicio de sesión mediante enlace mágico por correo electrónico, cookie de sesión firmada, encabezado CSRF sobre mutaciones, alcance de función/campaña
Desafío de inicio de sesión de administrador POST /admin/auth/start Verificación opcional de Cloudflare Turnstile antes de la emisión del enlace mágico del administrador
Reto de recordatorio de lanzamiento POST /launch-reminders Verificación opcional/esperada de Cloudflare Turnstile antes de escribir el recordatorio de registro
Secreto de recuperación del administrador Automatización y recuperación de puntos finales /admin/* Encabezado Authorization: Bearer <secret> o x-admin-key para operaciones basadas en scripts
Protección del modo de prueba /test/* APP_MODE === 'test' verificación del entorno

Almacenamiento de datos (Cloudflare KV)

Patrón clave Espacio de nombres Datos Sensibilidad
pledge:{orderId} PROMESAS Correo electrónico, importe, ID de Stripe, estado Alta - PII + datos de pago
email:{email} PROMESAS Matriz de ID de pedido Medio: vincula el correo electrónico a las promesas
stats:{slug} PROMESAS Totales agregados Bajo - público
tier-inventory:{slug} PROMESAS Recuentos de reclamos de nivel Bajo - público
stripe-event:{id} PROMESAS bandera “procesada” Bajo - idempotencia
campaign-pledges:{slug} PROMESAS Matriz de ID de pedido por campaña Bajo - índice
campaign-charged:{slug} PROMESAS Marca de tiempo de finalización de la liquidación Bajo - bandera
settlement-job:{slug} PROMESAS Progreso del lote de liquidación Bajo - efímero
pending-extras:{orderId} PROMESAS Artículo de soporte temporal/extras de pago de cantidad personalizada Bajo - efímero
pending-tiers:{orderId} PROMESAS Metadatos de nivel de desbordamiento temporal durante el pago Bajo - efímero
cron:lastRun PROMESAS Última marca de tiempo de ejecución cron horaria persistente Bajo - seguimiento
admin-login:{hash} PROMESAS Inicio de sesión único de administrador y correo electrónico Medio: autenticación de administrador efímera
admin-session:{hash} PROMESAS Correo electrónico de administrador, función, alcance de la campaña, token CSRF, vencimiento Alta - autenticación de administrador
admin-users:v1 PROMESAS Usuarios administradores de tiempo de ejecución y alcances de campaña Alto - control de acceso
admin-marketing-referrals:{slug} PROMESAS Metadatos del código de referencia guardado Bajo: datos de marketing escritos por el administrador
admin-audit:{date}:{action}:{id} PROMESAS Eventos recientes de auditoría de mutación de administrador Medio: identidad del administrador + metadatos operativos
launch-reminder:{slug}:{emailHash} PROMESAS Correo electrónico de recordatorio de próxima campaña y metadatos de suscripción Medio: correo electrónico relacionado con la campaña
launch-reminder-suppressed:{slug}:{emailHash} PROMESAS Marcador de supresión de recordatorio Medio: hash de correo electrónico con ámbito de campaña
launch-reminder-sent:{slug}:{emailHash} PROMESAS Recordatorio de envío de marcador de idempotencia Bajo - estado de envío
launch-reminder-dispatch:{slug} PROMESAS Cursor/progreso del trabajo de envío de recordatorio acotado Bajo - estado operativo
launch-reminder-dispatch-queue:v1 PROMESAS Cola de envío de recordatorio inactiva/marcador pendiente Bajo - estado operativo
supporter-email-retry:{orderId} PROMESAS Carga útil de reintento de correo electrónico de confirmación de colaborador en cola Medio: carga útil de correo electrónico de apoyo
supporter-email-retry-queue:v1 PROMESAS Reintento de correo electrónico del colaborador inactivo/pendiente y marcador de siguiente intento Bajo - estado operativo
add-on-inventory-sold:v1 PROMESAS Proyección de recuento de ventas de complementos de plataforma Bajo: estado del inventario agregado
vote:{slug}:{decision}:{email} VOTOS elección de voto Medio - vincula a un partidario para que vote
results:{slug}:{decision} VOTOS recuentos de votos Bajo - semipúblico
rl:{endpoint}:{ip} LÍMITE DE TARIFAS Recuento de solicitudes + tiempo de reinicio Bajo - efímero

La reserva escasa de nivel limitado y la verdad del recuento comprometido ya no se almacenan en KV. Ese estado sensible a la raza ahora reside en el coordinador de Objetos Durables por campaña, mientras que KV mantiene solo la proyección pública tier-inventory:{slug}.


Descripción general del refuerzo de seguridad

La postura de seguridad actual está diseñada en torno a algunos principios básicos:

  • mantener los precios, el estado de la promesa y el servidor de liquidación canónicos
  • alcance el acceso de los seguidores lo más estrictamente posible
  • falla cerrada cuando faltan secretos o comprobaciones del entorno
  • mantenga el almacenamiento del navegador y las respuestas almacenables en caché con baja sensibilidad de forma predeterminada
  • validar el contenido creado y solicitar cargas útiles antes de que alcancen una lógica sensible
  • Preservar la visibilidad operativa a través de pruebas de seguridad repetibles y manejo de secretos explícitos.

Control de acceso y control ambiental

  • Los enlaces mágicos están dirigidos a rutas de campaña y compromisos específicos en lugar de cuentas de usuario amplias.
  • El acceso de administrador privado utiliza enlaces mágicos de correo electrónico, cookies de sesión firmadas, comprobaciones CSRF y alcance de rol/campaña.
  • El inicio de sesión de administrador puede requerir un desafío de Cloudflare Turnstile antes de iniciar sesión, escribir nonce o entregar un enlace mágico.
  • Las rutas /test/* están cerradas en modo de prueba y no deben ser accesibles en implementaciones normales.
  • Las rutas de administración requieren un secreto explícito y están diseñadas para fallar cuando no se configuran correctamente.
  • La votación de los seguidores está vinculada a la identidad de correo electrónico del seguidor asociada con el compromiso autorizado, lo que evita una simple amplificación del voto de múltiples compromisos.

Protecciones de origen, administrador y webhook

  • El manejo del webhook de Stripe se basa en la verificación de firmas y un secreto configurado explícito
  • La comparación admin-secret es segura en lugar de utilizar una comparación directa ingenua
  • Los flujos POST sensibles del navegador, como el arranque del proceso de pago, la finalización y las actualizaciones de los métodos de pago, se verifican en origen con la base del sitio configurado.
  • Las superficies de devolución de llamada heredadas que ya no pertenecen al flujo de pago en vivo se eliminan intencionalmente en lugar de dejarlas inactivas.

Endurecimiento del navegador y de la respuesta

  • Las respuestas de finalización y arranque de pago específicas del pedido se entregan con Cache-Control: private, no-store
  • La persistencia de larga duración del navegador se limita a la estructura del carrito y a las entradas de precios, mientras que los borradores de contactos y direcciones permanecen en el ámbito de la sesión.
  • Los marcadores de recuperación de corta duración se utilizan para la continuidad del proceso de verificación en lugar de dejar el estado sensible en vuelo almacenado indefinidamente.
  • Los encabezados de respuesta de seguridad reducen el rastreo de MIME, el riesgo de encuadre y la filtración innecesaria de referencias.

Validación de entrada y contenido

  • Las cargas útiles de inicio de pago validan identificadores de campaña, direcciones de correo electrónico, artículos del carrito y entradas de contribución antes de la reconstrucción canónica.
  • Los puntos finales de votación validan los identificadores de decisiones y los valores de las opciones antes de que alcancen la lógica de cambio de estado.
  • La configuración del panel, los campos de la campaña, los bloques de contenido, los complementos, los niveles, los elementos de soporte, las entradas del diario, las decisiones y los registros de los usuarios se normalizan en el lado del servidor antes de la persistencia.
  • Las cargas de medios del panel tienen un alcance por función, acceso a la campaña, tipo de carga, tipo de contenido, tamaño de archivo, directorio de destino y nombre de archivo canónico.
  • Las etiquetas creadas por el creador y el contenido enriquecido se escapan o se desinfectan de forma predeterminada, y solo se conserva un subconjunto HTML muy pequeño incluido en la lista de permitidos.
  • las incrustaciones estructuradas se incluyen en la lista permitida para precisar los proveedores aprobados y las formas de URL en lugar de verificaciones amplias de subcadenas
  • Los destinos de los enlaces de rebajas están restringidos a esquemas seguros y enlaces internos.

Inventario e integridad de datos

  • El escaso inventario de nivel limitado se coordina a través de un objeto duradero por campaña en lugar de confiar en el estado KV visible para el cliente para conocer la verdad sensible a la raza.
  • El inventario público sigue siendo una proyección para lecturas eficientes, mientras que la reserva y el compromiso de verdad permanecen en el coordinador.
  • La finalización del pago invalida las estadísticas y el inventario almacenados en caché, por lo que las páginas restauradas no siguen mostrando totales de compromiso previo obsoletos.
  • la liquidación y los informes dependen de los registros de promesas propiedad del servidor en lugar de los totales enviados por el navegador

Controles de abuso y salvaguardias operativas

  • La limitación de tarifas está disponible para rutas costosas como pago, gestión de promesas, operaciones administrativas y webhooks.
  • Las solicitudes bloqueadas están diseñadas para fallar en el cierre sin convertir el abuso en escrituras KV adicionales excesivas.
  • Las lecturas normales del panel, los filtros, las vistas previas, los análisis, las descargas de informes y los borradores del editor local están diseñados para evitar escrituras KV.
  • los valores secretos permanecen en secretos de los trabajadores o en archivos locales ignorados; el panel puede informar el estado configurado/faltante pero no puede editar ni serializar valores secretos
  • Los conjuntos de pruebas de seguridad y auditoría secreta son parte de la ruta de verificación documentada.
  • El modelo de seguridad supone que los operadores mantendrán los secretos de implementación rotados, con alcance y fuera del historial del repositorio.

Límites aceptados

Algunas compensaciones siguen siendo intencionales en el modelo actual:

  • Los enlaces mágicos son duraderos porque la gestión de promesas sin cuentas debe seguir siendo utilizable en todos los cronogramas de la campaña.
  • Los tokens aún llegan a través de URL enviadas por correo electrónico, por lo que la plataforma se basa en un acceso con alcance, encabezados de respuesta y una persistencia limitada del navegador en lugar de un flujo completo de intercambio de tokens.

Si una implementación necesita una postura más estricta que la predeterminada, los siguientes pasos más probables serían una vida útil más corta de los tokens, flujos de reemisión de tokens más fáciles y un intercambio de tokens único que elimine los tokens sin procesar de las URL visibles después de su entrada.


Lista de verificación de secretos

Antes de implementar en producción, verifique que estos secretos estén configurados:

Secreto Variable de entorno Longitud mínima
Clave API de banda STRIPE_SECRET_KEY_LIVE N/A
Secreto del webhook de rayas STRIPE_WEBHOOK_SECRET_LIVE 32+ caracteres
Secreto de intención de pago CHECKOUT_INTENT_SECRET 32+ caracteres
Secreto del enlace mágico MAGIC_LINK_SECRET 32+ caracteres
Secreto del token de recordatorio de lanzamiento Reserva LAUNCH_REMINDER_TOKEN_SECRET o MAGIC_LINK_SECRET 32+ caracteres
Secreto de sesión de administrador ADMIN_SESSION_SECRET 32+ caracteres
Secreto de administrador ADMIN_SECRET 32+ caracteres
Secreto del torniquete TURNSTILE_SECRET_KEY, ADMIN_TURNSTILE_SECRET_KEY o LAUNCH_REMINDER_TURNSTILE_SECRET_KEY N/A
Reenviar clave API RESEND_API_KEY N/A

Generar secretos seguros:

openssl rand -base64 32

Pruebas de penetración

Consulte tests/security/README.md para conocer el conjunto de pruebas de penetración.

Ejecute pruebas de seguridad:

npm run test:secrets            # Audit local secret exposure in files + history
npm run test:security           # Against local Worker
npm run test:security:staging   # Against a staging worker, if you maintain one

npm run test:premerge ahora incluye la auditoría secreta automáticamente, por lo que la activación de combinación local verifica tanto el comportamiento de seguridad como la exposición accidental de credenciales.

Para ejecuciones locales, mantenga configurado CHECKOUT_INTENT_SECRET si desea que la suite de inicio de pago y pago del trabajador en vivo ejerza la ruta de firma propia real.


Respuesta a incidentes

Compromiso de token

Si un token de enlace mágico está comprometido:

  1. El token está vinculado a un ID de pedido/correo electrónico/campaña específico.
  2. Sólo puede acceder/modificar ese pedido autorizado.
  3. Para invalidar: elimine el compromiso de KV (GET /pledge luego devolverá 404 para ese token)
  4. Opcionalmente: regenerar MAGIC_LINK_SECRET (invalida TODOS los tokens)

Sesión de administración o compromiso secreto

  1. Gire inmediatamente ADMIN_SESSION_SECRET y ADMIN_SECRET a través de wrangler secret put
  2. Borrar claves admin-session:* activas del espacio de nombres Worker KV
  3. Revise los eventos admin-audit:* y las confirmaciones de GitHub para detectar acciones administrativas no autorizadas
  4. Vuelva a verificar las estadísticas de la campaña, los datos de las promesas, la configuración y los alcances de los usuarios administradores.

Compromiso secreto de Stripe Webhook

  1. Gire el secreto del webhook en Stripe Dashboard → Webhooks
  2. Actualizar STRIPE_WEBHOOK_SECRET_* en Worker
  3. Verifique si hay promesas sospechosas creadas durante la ventana de exposición

Webhook de Stripe perdido (Desarrollo)

Si el paso de pago en el sitio se completa pero el compromiso aún no aparece (común en desarrolladores locales cuando el reenvío del webhook se retrasa o no funciona):

  1. Verifique la salida de Stripe CLI para conocer el estado de entrega del webhook
  2. El cliente primero intentará /checkout-intent/complete automáticamente para la recuperación local, pero si el compromiso aún no aparece, use el punto final de recuperación del administrador para crearlo manualmente:
    curl -X POST http://localhost:8787/admin/recover-checkout \
      -H 'Authorization: Bearer YOUR_ADMIN_SECRET' \
      -H 'Content-Type: application/json' \
      -d '{"sessionId": "cs_test_..."}'
    
  3. El punto final obtiene la sesión de pago de Stripe y crea el compromiso si no existe.

Prevención:

  • Utilice scripts/dev.sh que ejecuta el trabajador con simulación KV local
  • scripts/dev.sh inicia un único oyente Stripe, reenvía eventos a 127.0.0.1:8787/webhooks/stripe, escribe el secreto whsec_... de ese mismo oyente en worker/.dev.vars y borra los procesos locales obsoletos en los puertos de desarrollo estándar antes del inicio.
  • Si inicia Stripe manualmente, use la misma instancia de escucha para reenviar y para el secreto que copia en la configuración local.
  • ./scripts/dev.sh --podman es la forma más fácil de mantener el límite de producción del sitio local/trabajador sin depender de la configuración del host Ruby/Wrangler.
  • Para realizar pruebas con datos inicializados, ejecute ./scripts/seed-all-campaigns.sh después de iniciar el trabajador.

Contactos de seguridad