Pentru echipele InfoSec și evaluatorii de aplicații

Securitatea în Peel

Pe scurt: conținutul board-ului e criptat în browser cu o cheie care stă în fragmentul linkului, așa că serverele noastre stochează și transmit text cifrat pe care nu-l pot citi. Iată exact cum funcționează și unde sunt limitele.

Rezumat

  • Textul cardurilor, numele, titlurile board-urilor, voturile și stickerele sunt criptate cu AES-256-GCM sub o cheie de board generată în browser. Cheia călătorește în fragmentul URL-ului (#k=) și nu e trimisă niciodată la un server.
  • Acțiunile facilitatorului, antetul board-ului și snapshoturile sunt semnate cu cheia Ed25519 a facilitatorului. Id-ul board-ului e derivat din acea cheie, așa că clienții detectează înlocuirea cheii.
  • Cardurile ascunse sunt sigilate pentru facilitator (X25519 sealed box) până la dezvăluire și legate prin angajamente cu salt, așa că textul dezvăluit nu poate fi înlocuit.
  • Anonim din start: operațiile nu conțin id de autor; voturile și reacțiile folosesc un id aleatoriu per board.
  • Participanții nu se autentifică niciodată. Fără SDK-uri de analytics, fără reclame, fără scripturi terțe, cu excepția Paddle.js pe pagina de plată.
  • Doar Cloudflare. Board-urile nerevendicate se șterg la 60 de zile după ultima vizită.

Cum circulă datele

Fragmentul unui URL (tot ce urmează după #) e gestionat de browser și nu face parte din cererea HTTP. Peel pune cheia board-ului acolo.

Fluxul datelor: cheia e citită local din link; prin rețea trec doar text cifrat și metadate minime.

Modelul de amenințări

Proiectăm totul astfel încât un server complet compromis sau curios, inclusiv noi, să afle cât mai puțin posibil despre ce spune o echipă.

Ce protejăm

  • Textul cardurilor, GIF-urile alese și, în modul cu nume, numele autorului.
  • Numele și avatarurile participanților, numele board-ului și titlurile șabloanelor personalizate.
  • Cine a votat sau a reacționat la ce și cine a scris fiecare card.
  • Integritatea retro-ului: pașii, dezvăluirile și deciziile facilitatorului nu pot fi falsificate.

Adversarii pe care îi luăm în calcul

  • Propriile noastre servere, un angajat rău-intenționat sau cineva care ne compromite stocarea: obțin doar text cifrat și metadate.
  • Un atacator în rețea: TLS peste tot; cheia nu trece niciodată prin rețea.
  • Un server care înlocuiește cheile: id-ul board-ului e derivat din cheia publică a facilitatorului, iar clienții verifică antetul semnat.
  • Un participant care vrea să tragă cu ochiul sau să falsifice: cardurile ascunse sunt sigilate pentru facilitator; operațiile facilitatorului necesită semnătura lui; editarea unui card necesită cheia cardului.
  • Un facilitator care republică pentru un autor offline: angajamentele îl împiedică să schimbe textul.

Ce nu acoperim, sincer

  • Oricine are linkul de participant poate citi board-ul. Linkul este controlul de acces. Partajează-l cu aceeași grijă ca board-ul însuși.
  • Un dispozitiv compromis, o extensie de browser rău-intenționată sau cineva care se uită peste umărul tău.
  • Analiza traficului: serverul vede momentele, dimensiunile și numerele (vezi harta datelor).
  • Blocarea serviciului de către server: poate pierde sau întârzia mesaje, dar nu le poate citi sau falsifica.

Criptografie

Toate primitivele vin din API-ul WebCrypto al browserului. Fără criptografie proprie, fără biblioteci criptografice terțe.

CheieAlgoritmFolosită pentru
K_boardAES-256-GCM, IV aleatoriu de 96 de bițiCriptează fiecare operație, snapshot și actualizare de prezență. Datele adiționale leagă id-ul board-ului, tipul operației și id-ul operației, astfel încât textul cifrat nu poate fi reluat într-un alt board sau slot. Există doar în fragmentul linkului #k=.
K_ownerEd25519Autoritatea facilitatorului. Semnează operațiile facilitatorului (pas, timer, reflector, șablon, setări, deschiderea votului, ascunderea unui card), antetul board-ului și snapshoturile. Un contor strict crescător blochează reluarea (replay). Doar în linkul de facilitator (&o=).
Id-ul board-uluiSHA-256, primii 80 de biți, base32Derivat din cheia publică a facilitatorului, așa că id-ul însuși dovedește ce cheie deține board-ul. Clienții resping un antet dacă hash-ul cheii din el nu corespunde id-ului.
K_facX25519 sealed box: X25519 efemer + HKDF-SHA256 + AES-256-GCMCardurile ascunse sunt sigilate cu cheia publică a facilitatorului cât timp oamenii scriu. Doar linkul de facilitator (&f=) le poate deschide.
Cheile cardurilorEd25519, o cheie per cardFiecare cheie e deținută doar de autor. Editările și dezvăluirea sunt semnate cu ea: autenticitate fără identitate.
Angajamente (commitments)HMAC-SHA256 cu un salt aleatoriu de 128 de bițiUn card ascuns publică un angajament față de conținutul său. La dezvăluire se publică conținutul și saltul, iar fiecare client le verifică, astfel încât nimeni nu poate înlocui textul.
PrezențăAES-256-GCM cu K_boardNumele, avatarurile și indicatorii de scriere sunt criptați ca orice altceva.
Pachetul de cheiHKDF-SHA256 din PRF-ul passkey-ului sau din codul de recuperare, AES-256-GCMFacilitatorii conectați pot sincroniza cheile board-urilor între dispozitive. Pachetul e criptat pe dispozitiv; serverul stochează doar text cifrat și chei împachetate.

Mesajele semnate sunt separate pe domenii (de exemplu peel/sig/v1, peel/header/v1, peel/snap/v1), așa că o semnătură făcută pentru un scop nu poate fi refolosită pentru altul.

Cardurile ascunse și dezvăluirea

  1. În timpul scrierii, browserul autorului sigilează cardul cu cheia X25519 a facilitatorului și publică un angajament cu salt. Textul în clar rămâne pe dispozitivul autorului.
  2. Ceilalți participanți văd un card ascuns: nu-l pot decripta, și nici serverul nu poate.
  3. Browserul facilitatorului poate deschide cardurile sigilate (previzualizarea „👁 doar tu”).
  4. La dezvăluire, fiecare autor republică cardul criptat cu K_board, semnat cu cheia cardului, împreună cu saltul.
  5. Dacă un autor e offline, facilitatorul republică în locul lui. Fiecare client verifică angajamentul, deci textul trebuie să corespundă cu ce a scris autorul.

Pentru că facilitatorul poate, tehnic, să previzualizeze, fiecare board le afișează tuturor eticheta „facilitatorul poate previzualiza” cât timp cardurile sunt ascunse. Nu e o setare care poate fi dezactivată.

Anonimat

  • Operațiile nu conțin niciun id de autor. Serverul nu poate lega un card de o persoană și, implicit, nici facilitatorul.
  • Voturile și reacțiile sunt asociate unui id aleatoriu per board, care nu e legat de un nume.
  • Indicatorii de scriere arată că cineva scrie, niciodată ce scrie.
  • Modul cu nume e o setare explicită a board-ului; în acest caz, numele autorului circulă în interiorul cardului criptat.
  • Participanții nu se conectează niciodată, deci nu există niciun cont care să poată fi corelat.

Harta datelor

Tot ce deținem, unde se află, cine le poate citi și cât timp le păstrăm.

DateUndeCine le poate citiPăstrare
Carduri, voturi, reacții, stickere, acțiuni, numele board-ului, titlurile șabloanelor personalizateStocarea Durable Object, ca text cifratCei care au linkul board-uluiPână la ștergerea board-ului
Plicul operației: tip, secvența și ora de pe server, dimensiune, contorul facilitatoruluiStocarea Durable ObjectPeelPână la ștergerea board-ului
Numele participanților, avatarurile, indicatorul de scriereTransmise ca text cifrat; ținute în memorie cât timp sunt conectațiCei care au linkul board-uluiCât timp sunt conectați
Starea board-ului: pasul, numărul de participanți și de carduri, id-ul șablonului din catalog, indicatorul de blocare, momentul creării și al expirăriiDurable Object, D1; mesaje Slack/Teams, dacă aplicația e instalatăPeel; canalul tău de Slack/TeamsPe durata de viață a board-ului
Cont de facilitator: id-ul de la furnizor, nume, e-mail dacă e partajat, furnizori asociați, data creării, sesiuneD1PeelPână îți ștergi contul
Pachetul de chei și cheile împachetateD1, ca text cifratDoar tu (passkey sau cod de recuperare)Până îți ștergi contul
Plan, starea abonamentului, id-urile de client și de abonament din PaddleD1; PaddlePeel, PaddleCât cere legislația fiscală
Id-urile de workspace, canal și mesaj din Slack/Teams; tokenul botului (criptat cu AES-GCM)D1PeelPână la dezinstalare
Adresa IPEdge-ul Cloudflare, temporar (livrare, limitarea ratei)Cloudflare, limitatorul de rată al PeelNu se stochează în bazele noastre de date
Contoare zilnice (board-uri create, pași atinși)D1PeelDoar agregat, fără identificatori

Ce poate și ce nu poate face serverul nostru

Poate

  • Să vadă tipurile operațiilor, dimensiunile, momentele, pasul, numărul de participanți și de carduri, id-ul șablonului din catalog și indicatorul de blocare.
  • Să piardă, să întârzie sau să refuze livrarea operațiilor (denial of service).
  • Să vadă adresele IP la marginea rețelei.
  • Să numere board-urile și pașii atinși, agregat.
  • Să șteargă un board (expirare, rapoarte de abuz).

Nu poate

  • Să citească textul cardurilor, numele, titlurile board-urilor, titlurile șabloanelor personalizate, voturile sau stickerele.
  • Să citească cardurile ascunse înainte de dezvăluire (și nici după).
  • Să falsifice acțiunile facilitatorului: acestea necesită semnătura facilitatorului.
  • Să strecoare propriile chei: id-ul board-ului l-ar da de gol.
  • Să schimbe textul dezvăluit al unui card: angajamentele și semnăturile cardurilor l-ar da de gol.
  • Să afle cine a scris fiecare card.

Ca la orice aplicație web, ai încredere în codul pe care îl servim. Păstrăm această suprafață mică: o Content Security Policy strictă, script-src limitat la propria noastră origine, fără scripturi terțe, iar protocolul poate fi analizat la cerere.

Slack și Microsoft Teams

  • Mesajele conțin linkul board-ului și metadate (titlu, pas, numere). Textul cardurilor e trimis doar când facilitatorul postează un rezumat și îl confirmă.
  • Cardul „Intră” conține linkul de participant, care include cheia. Oricine poate citi canalul respectiv poate intra, la fel ca atunci când lipești linkul acolo.
  • Previzualizările linkurilor arată doar pasul și numerele, niciodată conținutul.
  • Permisiuni Slack (scopes): commands, chat:write, links:read, links:write; users:read doar când o echipă activează mențiunile.
  • Tokenurile botului sunt criptate în repaus (AES-GCM, cu cheia păstrată ca secret de Worker). Semnăturile cererilor Slack și tokenurile Bot Framework sunt verificate la fiecare cerere.
  • Dezinstalarea aplicației oprește orice postare și îi șterge tokenurile.

Web și infrastructură

  • Doar Cloudflare: Pages, Workers, Durable Objects, D1, R2 și KV în contul 1801 Labs. Niciun alt server.
  • TLS peste tot, HSTS și o Content Security Policy strictă.
  • Fără SDK-uri de analytics, fără reclame, fără scripturi terțe. Singura excepție e Paddle.js, încărcat doar pe pagina de plată.
  • Un singur cookie, doar pentru facilitatorii conectați: peel_session, httpOnly, Secure, SameSite=Lax.
  • Căutarea de GIF-uri și fișierele media trec prin API-ul nostru, așa că adresele IP ale participanților nu ajung niciodată la GIPHY.
  • Fonturile și celelalte resurse sunt găzduite de noi. Fără fonturi de pe CDN, fără pixeli de urmărire.
  • Limite de rată per IP și per board; rapoarte de abuz și blocarea board-ului de către facilitator.

Conturi și păstrarea datelor

  • Participanții nu se autentifică niciodată. Facilitatorii se conectează cu Google (ori cu Entra ID prin Teams) doar ca să păstreze board-uri sau să folosească funcții plătite.
  • Un cont stochează un id, numele și e-mailul primite de la furnizor și un pachet de chei criptat, deblocat cu un passkey (WebAuthn PRF) sau cu un cod de recuperare. Conectarea oferă calitatea de proprietar, nu acces la conținut.
  • Revendicarea unui board dovedește proprietatea printr-o semnătură de facilitator. Cheile board-ului nu sunt trimise niciodată.
  • Board-urile fără cont se șterg la 60 de zile după ultima vizită; fiecare vizită repornește termenul. Alarmele Durable Object șterg stocarea.
  • Ștergerea contului șterge sesiunile, pachetul de chei și biblioteca ta și readuce board-urile tale la expirarea după 60 de zile.

Divulgare responsabilă

Ai găsit ceva? Spune-ne întâi nouă și lucrăm împreună. Confirmăm primirea rapoartelor în cel mult trei zile lucrătoare.

[email protected]

  • Te rugăm să ne lași un timp rezonabil (ținta noastră e de 90 de zile) pentru remediere înainte de a face publică problema.
  • Testează cu propriile board-uri și conturi. Nu accesa datele altor persoane și nu degrada serviciul.
  • Nu vom iniția acțiuni în justiție pentru cercetarea de bună-credință care respectă aceste reguli.
  • Contact în format citibil automat: /.well-known/security.txt.

Clienții planului Companie primesc un pachet pentru evaluarea de securitate (răspunsuri la chestionare, subprocesatori, fluxuri de date). DPA · Confidențialitate