Для команд інформаційної безпеки та рецензентів застосунків
Безпека в Peel
Коротко: вміст дошки шифрується в браузері ключем, який міститься у фрагменті посилання, тому наші сервери зберігають і пересилають шифротекст, який не можуть прочитати. Нижче — як саме це працює і де пролягають межі.
Підсумок
- Текст карток, імена, назви дошок, голоси та стікери шифруються алгоритмом AES-256-GCM ключем дошки, згенерованим у браузері. Ключ передається у фрагменті URL (
#k=) і ніколи не надсилається на сервер. - Дії фасилітатора, заголовок дошки та знімки стану підписуються ключем фасилітатора Ed25519. Ідентифікатор дошки виводиться з цього ключа, тож клієнти виявляють підміну ключа.
- Приховані картки запечатані для фасилітатора (X25519 sealed box) до відкриття і прив’язані солоними зобов’язаннями (commitments), тож відкритий текст неможливо підмінити.
- Анонімно за замовчуванням: операції не містять ідентифікатора автора; голоси й реакції використовують випадковий ідентифікатор, окремий для кожної дошки.
- Учасники ніколи не автентифікуються. Жодних SDK аналітики, реклами чи сторонніх скриптів, окрім Paddle.js на сторінці оплати.
- Лише Cloudflare. Незакріплені дошки видаляються через 60 днів після останнього відвідування.
Як рухаються дані
Фрагмент URL (усе після #) обробляє браузер, і він не входить до HTTP-запиту. Саме туди Peel кладе ключ дошки.
Модель загроз
Ми проєктуємо систему так, щоб повністю скомпрометований або надто цікавий сервер — зокрема й ми самі — дізнавався якомога менше про те, що каже команда.
Що ми захищаємо
- Текст карток, вибір GIF і, в іменному режимі, ім’я автора.
- Імена й аватари учасників, назву дошки та назви власних шаблонів.
- Хто за що голосував чи на що реагував і хто яку картку написав.
- Цілісність ретро: етапи, відкриття та рішення фасилітатора неможливо підробити.
Кого ми вважаємо зловмисниками
- Наші власні сервери, недобросовісний інсайдер або той, хто зламав наше сховище: вони отримують шифротекст і метадані.
- Атакувальник у мережі: TLS усюди; ключ ніколи не передається мережею.
- Сервер, що підміняє ключі: ідентифікатор дошки виводиться з публічного ключа фасилітатора, а клієнти перевіряють підписаний заголовок.
- Учасник, який хоче підглянути чи підробити: приховані картки запечатані для фасилітатора; операції фасилітатора потребують його підпису; редагування картки потребує ключа картки.
- Фасилітатор, що повторно публікує картку за автора, який офлайн: зобов’язання не дають змінити текст.
Чесно: що поза межами моделі
- Будь-хто з посиланням учасника може читати дошку. Посилання — це і є контроль доступу. Поширюй його так само обережно, як саму дошку.
- Скомпрометований пристрій, шкідливе розширення браузера або хтось, хто зазирає через плече.
- Аналіз трафіку: сервер бачить час, розміри та лічильники (див. карту даних).
- Відмова в обслуговуванні з боку сервера: він може відкидати чи затримувати повідомлення, але не читати й не підробляти їх.
Криптографія
Усі примітиви — з браузерного API WebCrypto. Жодної саморобної криптографії, жодних сторонніх криптобібліотек.
| Ключ | Алгоритм | Призначення |
|---|---|---|
| K_board | AES-256-GCM, випадковий 96-бітний IV | Шифрує кожну операцію, знімок стану й оновлення присутності. Додаткові дані (AAD) прив’язують ідентифікатор дошки, тип операції та її ідентифікатор, тож шифротекст неможливо повторно відтворити в іншій дошці чи слоті. Існує лише у фрагменті посилання #k=. |
| K_owner | Ed25519 | Повноваження фасилітатора. Підписує операції фасилітатора (етап, таймер, фокус, шаблон, налаштування, відкриття голосів, приховування картки), заголовок дошки та знімки стану. Строго зростаючий лічильник блокує повторне відтворення. Лише в посиланні фасилітатора (&o=). |
| Ідентифікатор дошки | SHA-256, перші 80 біт, base32 | Виводиться з публічного ключа фасилітатора, тож сам ідентифікатор доводить, якому ключу належить дошка. Клієнти відхиляють заголовок, ключ якого не дає цей ідентифікатор після гешування. |
| K_fac | X25519 sealed box: ефемерний X25519 + HKDF-SHA256 + AES-256-GCM | Поки учасники пишуть, приховані картки запечатуються публічним ключем фасилітатора. Відкрити їх можна лише з посиланням фасилітатора (&f=). |
| Ключі карток | Ed25519, окремий ключ для кожної картки | Є лише в автора. Ним підписуються редагування та відкриття: автентичність без розкриття особи. |
| Зобов’язання | HMAC-SHA256 із випадковою 128-бітною сіллю | Прихована картка публікує зобов’язання щодо свого вмісту. Під час відкриття публікуються вміст і сіль, і кожен клієнт їх перевіряє, тож ніхто не може підмінити текст. |
| Присутність | AES-256-GCM ключем K_board | Імена, аватари та індикатори набору тексту шифруються так само, як і все інше. |
| Пакет ключів | HKDF-SHA256 із PRF ключа доступу (passkey) або коду відновлення, AES-256-GCM | Фасилітатори з акаунтом можуть синхронізувати ключі дошок між пристроями. Пакет шифрується на пристрої; сервер зберігає лише шифротекст і обгорнуті ключі. |
Підписувані повідомлення розділено за доменами (наприклад, peel/sig/v1, peel/header/v1, peel/snap/v1), тож підпис для однієї мети неможливо використати для іншої.
Анонімність
- Операції не містять ідентифікатора автора. Сервер не може пов’язати картку з людиною, і за замовчуванням фасилітатор теж.
- Голоси й реакції прив’язані до випадкового ідентифікатора, окремого для кожної дошки й не пов’язаного з іменем.
- Індикатори набору тексту показують, що хтось пише, але ніколи — що саме.
- Іменний режим — це явне налаштування дошки; у ньому ім’я автора передається всередині зашифрованої картки.
- Учасники ніколи не входять в акаунт, тож немає жодного акаунта, який можна було б зіставити.
Карта даних
Усе, що ми зберігаємо: де це лежить, хто може це прочитати і як довго воно зберігається.
| Дані | Де | Хто може прочитати | Термін зберігання |
|---|---|---|---|
| Картки, голоси, реакції, стікери, дії, назва дошки, назви власних шаблонів | Сховище Durable Object, у вигляді шифротексту | Ті, хто має посилання на дошку | Доки дошку не видалено |
| Обгортка операції: тип, серверний порядковий номер і час, розмір, лічильник фасилітатора | Сховище Durable Object | Peel | Доки дошку не видалено |
| Імена учасників, аватари, індикатор набору | Пересилається як шифротекст; зберігається в пам’яті під час з’єднання | Ті, хто має посилання на дошку | Поки триває з’єднання |
| Статус дошки: етап, кількість учасників і карток, ідентифікатор шаблону з каталогу, позначка блокування, час створення й завершення строку | Durable Object, D1; повідомлення в Slack/Teams, якщо встановлено | Peel; ваш канал у Slack/Teams | Протягом життя дошки |
| Акаунт фасилітатора: ідентифікатор у провайдера, ім’я, email (якщо надано), пов’язані провайдери, дата створення, сесія | D1 | Peel | Доки акаунт не видалено |
| Пакет ключів і обгорнуті ключі | D1, у вигляді шифротексту | Лише власник (ключ доступу або код відновлення) | Доки акаунт не видалено |
| План, статус підписки, ідентифікатори клієнта й підписки в Paddle | D1; Paddle | Peel, Paddle | Стільки, скільки вимагає податкове законодавство |
| Ідентифікатори робочого простору, каналу й повідомлень у Slack/Teams; токен бота (зашифрований AES-GCM) | D1 | Peel | Доки застосунок не видалено |
| IP-адреса | Периферія Cloudflare, тимчасово (доставка, обмеження частоти запитів) | Cloudflare, обмежувач частоти запитів Peel | Не зберігається в наших базах даних |
| Щоденні лічильники (створені дошки, пройдені етапи) | D1 | Peel | Лише агреговано, без ідентифікаторів |
Що наш сервер може і чого не може
Він може
- Бачити типи операцій, розміри, час, етап, кількість учасників і карток, ідентифікатор шаблону з каталогу та позначку блокування.
- Відкидати, затримувати операції чи відмовлятися їх доставляти (відмова в обслуговуванні).
- Бачити IP-адреси на периферії мережі.
- Рахувати дошки та пройдені етапи в агрегованому вигляді.
- Видалити дошку (завершення строку, скарги на зловживання).
Він не може
- Читати текст карток, імена, назви дошок, назви власних шаблонів, голоси чи стікери.
- Читати приховані картки до відкриття (і навіть після нього).
- Підробляти дії фасилітатора: для них потрібен підпис фасилітатора.
- Підставити власні ключі: ідентифікатор дошки це викаже.
- Змінити відкритий текст картки: зобов’язання й підписи карток це викажуть.
- Визначити, хто яку картку написав.
Як і з будь-яким вебзастосунком, ви довіряєте коду, який ми віддаємо. Ми тримаємо цю поверхню мінімальною: сувора політика Content Security Policy, script-src обмежений нашим власним джерелом, жодних сторонніх скриптів, а протокол відкритий для аудиту на запит.
Slack і Microsoft Teams
- Повідомлення містять посилання на дошку та метадані (назва, етап, лічильники). Текст карток надсилається лише тоді, коли фасилітатор публікує підсумок і підтверджує це.
- Картка «Приєднатися» містить посилання учасника разом із ключем. Будь-хто, хто читає канал, може приєднатися — так само, як якщо вставити туди посилання.
- Розгорнуті прев’ю посилань показують лише етап і лічильники, ніколи не вміст.
- Дозволи Slack:
commands,chat:write,links:read,links:write;users:read— лише якщо команда вмикає згадки. - Токени ботів зберігаються в зашифрованому вигляді (AES-GCM, ключ зберігається як секрет Worker). Підписи запитів Slack і токени Bot Framework перевіряються в кожному запиті.
- Видалення застосунку зупиняє всі публікації та видаляє його токени.
Веб та інфраструктура
- Лише Cloudflare: Pages, Workers, Durable Objects, D1, R2 і KV в акаунті 1801 Labs. Жодних інших серверів.
- TLS усюди, HSTS і сувора політика Content Security Policy.
- Жодних SDK аналітики, реклами чи сторонніх скриптів. Єдиний виняток — Paddle.js, який завантажується лише на сторінці оплати.
- Одне cookie, лише для фасилітаторів, які увійшли:
peel_session, httpOnly, Secure, SameSite=Lax. - Пошук GIF і медіа проходять через наш API, тож IP-адреси учасників ніколи не потрапляють до GIPHY.
- Шрифти й ресурси розміщені на наших серверах. Жодних шрифтів із CDN, жодних пікселів відстеження.
- Обмеження частоти запитів для кожної IP-адреси й кожної дошки; скарги на зловживання та блокування дошки фасилітатором.
Акаунти та зберігання даних
- Учасники ніколи не автентифікуються. Фасилітатори входять через Google (чи Entra ID через Teams) лише для того, щоб зберігати дошки або користуватися платними функціями.
- Акаунт зберігає ідентифікатор, ім’я та email від провайдера, а також зашифрований пакет ключів, який розблоковується ключем доступу (WebAuthn PRF) або кодом відновлення. Вхід в акаунт дає право власності, а не доступ до вмісту.
- Закріплення дошки за акаунтом підтверджує право власності підписом фасилітатора. Ключі дошки ніколи не надсилаються.
- Дошки без акаунта видаляються через 60 днів після останнього відвідування; кожне відвідування запускає відлік заново. Сховище видаляють будильники Durable Object.
- Видалення акаунта видаляє сесії, пакет ключів і бібліотеку та повертає дошкам 60-денний строк зберігання.
Відповідальне розкриття
Знайшли вразливість? Повідомте спершу нам, і ми розберемося разом. Ми підтверджуємо отримання звітів протягом трьох робочих днів.
- Будь ласка, дайте нам розумний час на виправлення (ми прагнемо вкластися в 90 днів), перш ніж розкривати інформацію.
- Тестуйте на власних дошках і акаунтах. Не отримуйте доступу до чужих даних і не погіршуйте роботу сервісу.
- Ми не переслідуватимемо в судовому порядку сумлінні дослідження, що відповідають цим правилам.
- Машинозчитуваний контакт:
/.well-known/security.txt.
Клієнти плану «Компанія» отримують пакет для перевірки безпеки (відповіді на опитувальники, субпроцесори, потоки даних). DPA · Конфіденційність