Para equipos de seguridad y revisores de apps
Seguridad en Peel
La versión corta: el contenido del tablero se cifra en el navegador con una clave que vive en el fragmento del enlace, así que nuestros servidores guardan y retransmiten texto cifrado que no pueden leer. Aquí explicamos exactamente cómo, y dónde están los límites.
Resumen
- El texto de las tarjetas, los nombres, los títulos de los tableros, los votos y los stickers se cifran con AES-256-GCM mediante una clave de tablero generada en el navegador. La clave viaja en el fragmento de la URL (
#k=) y nunca se envía a un servidor. - Las acciones del facilitador, la cabecera del tablero y las instantáneas se firman con la clave Ed25519 del facilitador. El ID del tablero se deriva de esa clave, así que los clientes detectan cualquier sustitución de clave.
- Las tarjetas ocultas están selladas para el facilitador (sealed box X25519) hasta la revelación, y vinculadas mediante compromisos con sal para que el texto revelado no pueda cambiarse.
- Anónimo por defecto: las operaciones no llevan ID de autor; los votos y reacciones usan un ID aleatorio por tablero.
- Los participantes nunca se autentican. Sin SDK de analítica, sin anuncios, sin scripts de terceros salvo Paddle.js en la página de pago.
- Solo Cloudflare. Los tableros sin reclamar se eliminan 60 días después de la última visita.
Cómo fluyen los datos
El fragmento de una URL (todo lo que va después del #) lo gestiona el navegador y no forma parte de la petición HTTP. Ahí es donde Peel pone la clave del tablero.
Modelo de amenazas
Diseñamos para que un servidor totalmente comprometido o curioso, incluidos nosotros, sepa lo menos posible de lo que dice un equipo.
Qué protegemos
- El texto de las tarjetas, los GIF elegidos y, en el modo con nombre, el nombre de quien escribe.
- Los nombres y avatares de los participantes, el nombre del tablero y los títulos de plantillas personalizadas.
- Quién votó o reaccionó a qué, y quién escribió cada tarjeta.
- La integridad de la retro: las fases, las revelaciones y las decisiones del facilitador no se pueden falsificar.
Adversarios que tenemos en cuenta
- Nuestros propios servidores, alguien de dentro con malas intenciones o quien acceda a nuestro almacenamiento: obtienen texto cifrado y metadatos.
- Un atacante en la red: TLS en todas partes; la clave nunca cruza la red.
- Un servidor que sustituye claves: el ID del tablero se deriva de la clave pública del facilitador y los clientes verifican la cabecera firmada.
- Un participante que quiere fisgar o falsificar: las tarjetas ocultas están selladas para el facilitador; las operaciones del facilitador requieren su firma; editar una tarjeta requiere la clave de esa tarjeta.
- Un facilitador que republica por una persona desconectada: los compromisos le impiden cambiar el texto.
Fuera de alcance, con honestidad
- Cualquiera que tenga el enlace de participante puede leer el tablero. El enlace es el control de acceso. Compártelo con el mismo cuidado que el propio tablero.
- Un dispositivo comprometido, una extensión maliciosa del navegador o alguien mirando por encima del hombro.
- El análisis de tráfico: el servidor ve tiempos, tamaños y recuentos (consulta el mapa de datos).
- La denegación de servicio por parte del servidor: puede descartar o retrasar mensajes, pero no leerlos ni falsificarlos.
Criptografía
Todas las primitivas vienen de la API WebCrypto del navegador. Sin criptografía casera ni bibliotecas criptográficas de terceros.
| Clave | Algoritmo | Uso |
|---|---|---|
| K_board | AES-256-GCM, IV aleatorio de 96 bits | Cifra cada operación, instantánea y actualización de presencia. Los datos adicionales vinculan el ID del tablero, el tipo de operación y el ID de la operación, así que un texto cifrado no puede reutilizarse en otro tablero u otra posición. Vive solo en el fragmento del enlace #k=. |
| K_owner | Ed25519 | La autoridad del facilitador. Firma las operaciones del facilitador (fase, temporizador, foco, plantilla, ajustes, apertura de votos, ocultar una tarjeta), la cabecera del tablero y las instantáneas. Un contador estrictamente creciente impide la repetición. Solo en el enlace de facilitador (&o=). |
| ID del tablero | SHA-256, primeros 80 bits, base32 | Se deriva de la clave pública del facilitador, así que el propio ID demuestra qué clave es dueña del tablero. Los clientes rechazan una cabecera cuya clave no genere ese ID. |
| K_fac | Sealed box X25519: X25519 efímera + HKDF-SHA256 + AES-256-GCM | Las tarjetas ocultas se sellan con la clave pública del facilitador mientras se escribe. Solo el enlace de facilitador (&f=) puede abrirlas. |
| Claves de tarjeta | Ed25519, una clave por tarjeta | Solo la tiene quien escribió la tarjeta. Las ediciones y la revelación se firman con ella: autenticidad sin identidad. |
| Compromisos | HMAC-SHA256 con una sal aleatoria de 128 bits | Una tarjeta oculta publica un compromiso sobre su contenido. En la revelación se publican el contenido y la sal, y cada cliente los comprueba, así que nadie puede cambiar el texto. |
| Presencia | AES-256-GCM con K_board | Los nombres, avatares e indicadores de escritura se cifran como todo lo demás. |
| Paquete de claves | HKDF-SHA256 a partir del PRF de la llave de acceso o del código de recuperación, AES-256-GCM | Los facilitadores con sesión iniciada pueden sincronizar sus claves de tablero entre dispositivos. El paquete se cifra en el dispositivo; el servidor solo guarda texto cifrado y claves envueltas. |
Los mensajes de firma están separados por dominio (por ejemplo peel/sig/v1, peel/header/v1, peel/snap/v1) para que una firma hecha para un propósito no pueda reutilizarse para otro.
Anonimato
- Las operaciones no llevan ID de autor. El servidor no puede vincular una tarjeta con una persona y, por defecto, el facilitador tampoco.
- Los votos y reacciones se asocian a un ID aleatorio por tablero que no está vinculado a ningún nombre.
- Los indicadores de escritura muestran que alguien escribe, nunca qué.
- El modo con nombre es un ajuste explícito del tablero; en ese caso, el nombre del autor viaja dentro de la tarjeta cifrada.
- Los participantes nunca inician sesión, así que no hay ninguna cuenta que cruzar.
Mapa de datos
Todo lo que guardamos, dónde está, quién puede leerlo y cuánto tiempo se conserva.
| Datos | Dónde | Quién puede leerlos | Conservación |
|---|---|---|---|
| Tarjetas, votos, reacciones, stickers, acciones, nombre del tablero, títulos de plantillas personalizadas | Almacenamiento de Durable Object, como texto cifrado | Quien tenga el enlace del tablero | Hasta que se elimina el tablero |
| Sobre de la operación: tipo, secuencia y hora del servidor, tamaño, contador del facilitador | Almacenamiento de Durable Object | Peel | Hasta que se elimina el tablero |
| Nombres y avatares de los participantes, escritura en curso | Retransmitidos como texto cifrado; en memoria mientras dura la conexión | Quien tenga el enlace del tablero | Mientras dura la conexión |
| Estado del tablero: fase, número de participantes y tarjetas, ID de plantilla del catálogo, indicador de bloqueo, fechas de creación y caducidad | Durable Object, D1; mensajes de Slack/Teams si está instalado | Peel; tu canal de Slack/Teams | Vida del tablero |
| Cuenta de facilitador: ID del proveedor, nombre, correo si se comparte, proveedores vinculados, fecha de creación, sesión | D1 | Peel | Hasta que eliminas la cuenta |
| Paquete de claves y claves envueltas | D1, como texto cifrado | Solo tú (llave de acceso o código de recuperación) | Hasta que eliminas la cuenta |
| Plan, estado de la suscripción, IDs de cliente y de suscripción de Paddle | D1; Paddle | Peel, Paddle | Lo que exija la normativa fiscal |
| IDs de espacio de trabajo, canal y mensaje de Slack/Teams; token del bot (cifrado con AES-GCM) | D1 | Peel | Hasta la desinstalación |
| Dirección IP | Edge de Cloudflare, de forma transitoria (entrega, límites de uso) | Cloudflare, el limitador de uso de Peel | No se guarda en nuestras bases de datos |
| Contadores diarios (tableros creados, fases alcanzadas) | D1 | Peel | Solo agregados, sin identificadores |
Qué puede y qué no puede hacer nuestro servidor
Puede
- Ver los tipos de operación, tamaños, tiempos, la fase, el número de participantes y tarjetas, el ID de plantilla del catálogo y el indicador de bloqueo.
- Descartar, retrasar o negarse a entregar operaciones (denegación de servicio).
- Ver direcciones IP en el borde de la red.
- Contar tableros y fases alcanzadas, de forma agregada.
- Eliminar un tablero (caducidad, denuncias de abuso).
No puede
- Leer el texto de las tarjetas, nombres, títulos de tableros, títulos de plantillas personalizadas, votos ni stickers.
- Leer las tarjetas ocultas antes de la revelación (ni siquiera después).
- Falsificar acciones del facilitador: requieren su firma.
- Colar sus propias claves: el ID del tablero lo delataría.
- Cambiar el texto revelado de una tarjeta: los compromisos y las firmas de tarjeta lo delatarían.
- Saber quién escribió cada tarjeta.
Como en cualquier app web, confías en el código que servimos. Mantenemos esa superficie pequeña: una Content Security Policy estricta, script-src limitado a nuestro propio origen, ningún script de terceros, y el protocolo está abierto a revisión si se solicita.
Slack y Microsoft Teams
- Los mensajes llevan el enlace del tablero y metadatos (título, fase, recuentos). El texto de las tarjetas solo se envía cuando el facilitador publica un resumen y lo confirma.
- La tarjeta para unirse contiene el enlace de participante, que incluye la clave. Cualquiera que pueda leer ese canal puede unirse, igual que si se pegara allí el enlace.
- Las vistas previas de enlaces solo muestran la fase y los recuentos, nunca el contenido.
- Permisos de Slack:
commands,chat:write,links:read,links:write;users:readsolo cuando un equipo activa las menciones. - Los tokens de bot se cifran en reposo (AES-GCM, con la clave guardada como secreto del Worker). Las firmas de las peticiones de Slack y los tokens de Bot Framework se verifican en cada petición.
- Al desinstalar la app se dejan de publicar mensajes y se eliminan sus tokens.
Web e infraestructura
- Solo Cloudflare: Pages, Workers, Durable Objects, D1, R2 y KV en la cuenta de 1801 Labs. Ningún otro servidor.
- TLS en todas partes, HSTS y una Content Security Policy estricta.
- Sin SDK de analítica, sin anuncios, sin scripts de terceros. La única excepción es Paddle.js, que se carga solo en la página de pago.
- Una sola cookie, solo para facilitadores con sesión iniciada:
peel_session, httpOnly, Secure, SameSite=Lax. - La búsqueda de GIF y los archivos multimedia pasan por nuestra API, así que las direcciones IP de los participantes nunca llegan a GIPHY.
- Fuentes y recursos alojados por nosotros. Sin fuentes de CDN ni píxeles de seguimiento.
- Límites de uso por IP y por tablero; denuncias de abuso y bloqueo por parte del facilitador.
Cuentas y conservación
- Los participantes nunca se autentican. Los facilitadores inician sesión con Google (o Entra ID a través de Teams) solo para conservar tableros o usar funciones de pago.
- Una cuenta guarda un ID, el nombre y el correo del proveedor, y un paquete de claves cifrado que se desbloquea con una llave de acceso (WebAuthn PRF) o un código de recuperación. Iniciar sesión da la propiedad, no acceso al contenido.
- Reclamar un tablero demuestra la propiedad con una firma del facilitador. Las claves del tablero nunca se envían.
- Los tableros sin cuenta se eliminan 60 días después de la última visita; cada visita reinicia el plazo. Las alarmas de Durable Object borran el almacenamiento.
- Eliminar tu cuenta borra las sesiones, el paquete de claves y tu biblioteca, y devuelve a tus tableros la caducidad de 60 días.
Divulgación responsable
¿Has encontrado algo? Avísanos primero y trabajaremos contigo. Confirmamos la recepción de los informes en un plazo de tres días laborables.
- Danos un plazo razonable (nuestro objetivo son 90 días) para corregirlo antes de hacerlo público.
- Haz pruebas con tus propios tableros y cuentas. No accedas a datos de otras personas ni degrades el servicio.
- No emprenderemos acciones legales contra investigaciones de buena fe que sigan estas reglas.
- Contacto legible por máquina:
/.well-known/security.txt.
Los clientes del plan Empresa reciben un paquete de revisión de seguridad (respuestas a cuestionarios, subencargados, flujos de datos). DPA · Privacidad