Til InfoSec-teams og app-reviewere

Sikkerhed i Peel

Kort fortalt: tavlens indhold krypteres i browseren med en nøgle, der ligger i linkets fragment, så vores servere gemmer og videresender krypteret tekst, de ikke kan læse. Her er præcis hvordan, og hvor grænserne går.

Opsummering

  • Tekst fra kort, navne, tavletitler, stemmer og stickers krypteres med AES-256-GCM under en tavlenøgle, der genereres i browseren. Nøglen rejser i URL-fragmentet (#k=) og sendes aldrig til en server.
  • Facilitatorhandlinger, tavlens header og snapshots signeres med facilitatorens Ed25519-nøgle. Tavle-id’et udledes af den nøgle, så klienterne opdager, hvis nøglen bliver skiftet ud.
  • Skjulte kort er forseglet til facilitatoren (X25519 sealed box) indtil afsløringen og bundet af saltede commitments, så den afslørede tekst ikke kan byttes ud.
  • Anonymt som standard: operationer indeholder intet forfatter-id; stemmer og reaktioner bruger et tilfældigt id pr. tavle.
  • Deltagere autentificerer sig aldrig. Ingen analyse-SDK’er, ingen reklamer, ingen tredjepartsscripts bortset fra Paddle.js på betalingssiden.
  • Kun Cloudflare. Tavler uden ejer slettes 60 dage efter sidste besøg.

Sådan flyder data

Fragmentet i en URL (alt efter #) håndteres af browseren og er ikke en del af HTTP-forespørgslen. Det er der, Peel lægger tavlens nøgle.

Dataflow: nøglen læses lokalt fra linket; kun krypteret tekst og minimal metadata krydser netværket.

Trusselsmodel

Vi designer sådan, at en fuldt kompromitteret eller nysgerrig server, inklusive os selv, lærer så lidt som muligt om, hvad et team siger.

Det beskytter vi

  • Tekst fra kort, valg af GIF’er og, i tilstanden med navn, forfatterens navn.
  • Deltagernes navne og avatarer, tavlens navn og titler på egne skabeloner.
  • Hvem der stemte på eller reagerede på hvad, og hvem der skrev hvilket kort.
  • Retroens integritet: trin, afsløringer og facilitatorens beslutninger kan ikke forfalskes.

Modstandere, vi tager højde for

  • Vores egne servere, en ondsindet insider eller nogen, der bryder ind i vores lager: de får krypteret tekst og metadata.
  • En angriber på netværket: TLS overalt; nøglen krydser aldrig netværket.
  • En server, der bytter nøgler ud: tavle-id’et udledes af facilitatorens offentlige nøgle, og klienterne verificerer den signerede header.
  • En deltager, der vil kigge eller forfalske: skjulte kort er forseglet til facilitatoren; facilitatoroperationer kræver facilitatorens signatur; redigering af kort kræver kortets nøgle.
  • En facilitator, der genudgiver for en forfatter, der er offline: commitments forhindrer, at teksten ændres.

Uden for rammerne, helt ærligt

  • Alle, der har deltagerlinket, kan læse tavlen. Linket er adgangskontrollen. Del det, som du ville dele selve tavlen.
  • En kompromitteret enhed, en ondsindet browserudvidelse eller nogen, der kigger dig over skulderen.
  • Trafikanalyse: serveren ser tidspunkter, størrelser og antal (se datakortet).
  • Tjenestenægtelse fra serveren: den kan droppe eller forsinke beskeder, men ikke læse eller forfalske dem.

Kryptografi

Alle primitiver kommer fra browserens WebCrypto API. Ingen hjemmelavet kryptografi, ingen kryptobiblioteker fra tredjepart.

NøgleAlgoritmeBruges til
K_boardAES-256-GCM, tilfældig 96-bit IVKrypterer hver operation, hvert snapshot og hver tilstedeværelsesopdatering. De ekstra data binder tavle-id, operationstype og operations-id, så krypteret tekst ikke kan genafspilles i en anden tavle eller plads. Findes kun i linkets fragment #k=.
K_ownerEd25519Facilitatorens autoritet. Signerer facilitatoroperationer (trin, nedtælling, fokus, skabelon, indstillinger, åbning af stemmer, skjulning af et kort), tavlens header og snapshots. En strengt stigende tæller forhindrer genafspilning. Findes kun i facilitatorlinket (&o=).
Tavle-idSHA-256, de første 80 bit, base32Udledes af facilitatorens offentlige nøgle, så id’et i sig selv beviser, hvilken nøgle der ejer tavlen. Klienterne afviser en header, hvis nøgle ikke hasher til id’et.
K_facX25519 sealed box: flygtig X25519 + HKDF-SHA256 + AES-256-GCMSkjulte kort forsegles til facilitatorens offentlige nøgle, mens folk skriver. Kun facilitatorlinket (&f=) kan åbne dem.
KortnøglerEd25519, én nøgle pr. kortKun forfatteren har den. Redigeringer og afsløringen signeres med den: ægthed uden identitet.
CommitmentsHMAC-SHA256 med et tilfældigt 128-bit saltEt skjult kort offentliggør et commitment til sit indhold. Ved afsløringen offentliggøres indhold og salt, og alle klienter tjekker dem, så ingen kan bytte teksten ud.
TilstedeværelseAES-256-GCM under K_boardNavne, avatarer og skriveindikatorer krypteres ligesom alt andet.
NøglebundtHKDF-SHA256 fra adgangsnøglens PRF eller gendannelseskode, AES-256-GCMFacilitatorer, der er logget ind, kan synkronisere tavlenøgler på tværs af enheder. Bundtet krypteres på enheden; serveren gemmer kun krypteret tekst og indpakkede nøgler.

Signeringsbeskeder er domæneadskilte (f.eks. peel/sig/v1, peel/header/v1, peel/snap/v1), så en signatur til ét formål ikke kan genbruges til et andet.

Skjulte kort og afsløringen

  1. Mens forfatteren skriver, forsegler browseren kortet til facilitatorens X25519-nøgle og offentliggør et saltet commitment. Klarteksten bliver på forfatterens enhed.
  2. De andre deltagere ser et skjult kort: de kan ikke dekryptere det, og det kan serveren heller ikke.
  3. Facilitatorens browser kan åbne forseglede kort (forhåndsvisningen »👁 kun dig«).
  4. Ved afsløringen genudgiver hver forfatter kortet krypteret med K_board og signeret med kortnøglen sammen med saltet.
  5. Hvis en forfatter er offline, genudgiver facilitatoren for vedkommende. Alle klienter tjekker commitmentet, så teksten skal stemme med det, forfatteren skrev.

Fordi facilitatoren teknisk set kan forhåndsvise, viser alle tavler mærkaten »facilitatoren kan forhåndsvise« for alle, mens kortene er skjulte. Det er ikke en indstilling, der kan skjules.

Anonymitet

  • Operationer indeholder intet forfatter-id. Serveren kan ikke koble et kort til en person, og som standard kan facilitatoren heller ikke.
  • Stemmer og reaktioner er knyttet til et tilfældigt id pr. tavle, der ikke er koblet til et navn.
  • Skriveindikatorer viser, at nogen skriver, aldrig hvad.
  • Tilstanden med navn er en udtrykkelig tavleindstilling; forfatterens navn rejser så inde i det krypterede kort.
  • Deltagere logger aldrig ind, så der er ingen konto at koble sammen.

Datakort

Alt, hvad vi har, hvor det ligger, hvem der kan læse det, og hvor længe det bliver.

DataHvorKan læses afGemmes
Kort, stemmer, reaktioner, stickers, handlinger, tavlens navn, titler på egne skabelonerDurable Object-lager, som krypteret tekstPersoner med tavlens linkIndtil tavlen slettes
Operationens kuvert: type, serverens sekvensnummer og tidspunkt, størrelse, facilitatortællerDurable Object-lagerPeelIndtil tavlen slettes
Deltagernes navne, avatarer, skriveaktivitetVideresendes som krypteret tekst; holdes i hukommelsen, mens man er forbundetPersoner med tavlens linkMens man er forbundet
Tavlestatus: trin, antal deltagere og kort, katalogets skabelon-id, låseflag, oprettelses- og udløbstidspunktDurable Object, D1; Slack-/Teams-beskeder, hvis installeretPeel; jeres Slack-/Teams-kanalTavlens levetid
Facilitatorkonto: udbyder-id, navn, e-mail hvis delt, tilknyttede udbydere, oprettelsesdato, sessionD1PeelIndtil du sletter kontoen
Nøglebundt og indpakkede nøglerD1, som krypteret tekstKun dig (adgangsnøgle eller gendannelseskode)Indtil du sletter kontoen
Abonnement, abonnementsstatus, Paddle-kunde- og abonnements-id’erD1; PaddlePeel, PaddleSå længe skattelovgivningen kræver
Slack-/Teams-workspace, kanal- og besked-id’er; bot-token (AES-GCM-krypteret)D1PeelIndtil appen afinstalleres
IP-adresseCloudflares edge, midlertidigt (levering, hastighedsbegrænsning)Cloudflare, Peels hastighedsbegrænserGemmes ikke i vores databaser
Daglige tællere (oprettede tavler, nåede trin)D1PeelKun samlet, uden identifikatorer

Hvad vores server kan og ikke kan

Den kan

  • Se operationstyper, størrelser, tidspunkter, trinnet, antal deltagere og kort, katalogets skabelon-id og låseflaget.
  • Droppe, forsinke eller nægte at levere operationer (tjenestenægtelse).
  • Se IP-adresser i netværkets kant.
  • Tælle tavler og nåede trin, samlet.
  • Slette en tavle (udløb, anmeldelser af misbrug).

Den kan ikke

  • Læse tekst fra kort, navne, tavletitler, titler på egne skabeloner, stemmer eller stickers.
  • Læse skjulte kort før afsløringen (heller ikke efter).
  • Forfalske facilitatorhandlinger: de kræver facilitatorens signatur.
  • Skifte sine egne nøgler ind: tavle-id’et afslører det.
  • Ændre et korts afslørede tekst: commitments og kortsignaturer afslører det.
  • Se, hvem der skrev hvilket kort.

Som med enhver webapp stoler du på den kode, vi leverer. Vi holder den flade lille: en streng Content Security Policy, script-src begrænset til vores eget origin, ingen tredjepartsscripts, og protokollen kan gennemgås efter anmodning.

Slack og Microsoft Teams

  • Beskeder indeholder tavlens link og metadata (titel, trin, antal). Tekst fra kort sendes kun, når facilitatoren sender en opsummering og bekræfter den.
  • Deltag-kortet indeholder deltagerlinket, som inkluderer nøglen. Alle, der kan læse kanalen, kan være med, præcis som hvis man satte linket ind der.
  • Linkforhåndsvisninger viser kun trin og antal, aldrig indhold.
  • Slack-scopes: commands, chat:write, links:read, links:write; users:read kun når et team slår omtaler til.
  • Bot-tokens er krypterede i hvile (AES-GCM, nøglen opbevares som en Worker-secret). Slacks anmodningssignaturer og Bot Framework-tokens verificeres ved hver anmodning.
  • Afinstalleres appen, stopper alle beskeder, og dens tokens slettes.

Web og infrastruktur

  • Kun Cloudflare: Pages, Workers, Durable Objects, D1, R2 og KV på 1801 Labs’ konto. Ingen andre servere.
  • TLS overalt, HSTS og en streng Content Security Policy.
  • Ingen analyse-SDK’er, ingen reklamer, ingen tredjepartsscripts. Den eneste undtagelse er Paddle.js, som kun indlæses på betalingssiden.
  • Én cookie, kun for facilitatorer, der er logget ind: peel_session, httpOnly, Secure, SameSite=Lax.
  • GIF-søgning og medier går gennem vores API, så deltagernes IP-adresser aldrig når GIPHY.
  • Skrifttyper og filer hostes af os selv. Ingen CDN-skrifttyper, ingen sporingspixels.
  • Hastighedsbegrænsning pr. IP og pr. tavle; anmeldelse af misbrug og en facilitatorlås.

Konti og opbevaring

  • Deltagere autentificerer sig aldrig. Facilitatorer logger ind med Google (eller Entra ID via Teams) kun for at gemme tavler eller bruge betalte funktioner.
  • En konto gemmer et id, navnet og e-mailen fra udbyderen og et krypteret nøglebundt, der låses op med en adgangsnøgle (WebAuthn PRF) eller en gendannelseskode. At logge ind giver ejerskab, ikke adgang til indhold.
  • Når man gør krav på en tavle, bevises ejerskabet med en facilitatorsignatur. Tavlenøgler sendes aldrig.
  • Tavler uden konto slettes 60 dage efter sidste besøg; hvert besøg nulstiller uret. Alarmer i Durable Objects sletter lageret.
  • Sletter du din konto, slettes sessioner, nøglebundtet og dit bibliotek, og dine tavler får igen udløb efter 60 dage.

Ansvarlig offentliggørelse

Har du fundet noget? Fortæl os det først, så samarbejder vi med dig. Vi bekræfter modtagelsen af rapporter inden for tre hverdage.

[email protected]

  • Giv os rimelig tid (vi sigter efter 90 dage) til at rette fejlen, før du offentliggør den.
  • Test med dine egne tavler og konti. Tilgå ikke andres data, og forring ikke tjenesten.
  • Vi anlægger ikke sag mod research i god tro, der følger disse regler.
  • Maskinlæsbar kontakt: /.well-known/security.txt.

Kunder på Company-abonnementet får en sikkerhedspakke til vurdering (svar på spørgeskemaer, underdatabehandlere, dataflows). Databehandleraftale · Privatliv