Безопасность

Как ваши приложения остаются закрытыми и разделёнными

Авторам ничего из этого писать не нужно. Вход, доступ только для сотрудников, отдельные данные для каждого приложения и защита от поисковиков появляются у приложения при первой публикации. Ниже конкретика для тех, кому нужно это согласовать. TeamOnly в раннем доступе. Эта страница описывает, как всё устроено и работает сегодня, и мы обновляем её точно по мере изменений. Мы не заявляем сертификаты, которых у нас нет.

Каждый запрос сначала проходит вход

Весь трафик приложений на *.teamonly.app сначала попадает в один dispatch Worker. Он определяет организацию и приложение по имени хоста, проверяет действующую сессию и только после этого передаёт запрос приложению. Без сессии посетитель получает экран входа. Код клиента для него вообще не запускается.

Войти можно тремя способами: Google OpenID Connect, Microsoft (Entra ID) OpenID Connect или одноразовый код, отправленный на корпоративный адрес почты. Для Google и Microsoft ID-токен проверяется на нашем сервере: подпись, audience, срок действия и домен почты. Значение hd от Google берётся из проверенного токена, а не принимается на веру из запроса. Для Microsoft принимаются только рабочие и учебные учётные записи, а issuer токена должен совпадать с его собственным тенантом.

Коды на почту

Вместо Google или Microsoft коллега может запросить 6-значный код на рабочий адрес. Так TeamOnly пользуются компании, чья почта не на Google Workspace и не на Microsoft 365, без всякой настройки. Код доказывает, что человек управляет почтовым ящиком на одном из доменов вашей компании, то же самое, что доказывает вход через Google или Microsoft. Это работает с любым почтовым сервисом. Личные адреса, такие как Gmail или Outlook.com, для приложений компании не принимаются.

  • Код действует 10 минут.
  • После пяти неверных попыток код перестаёт работать. Нужно запросить новый.
  • Код работает только в том браузере, где его запросили. Подсмотренный через плечо или пересланный код в другом браузере бесполезен.
  • Число запросов ограничено для каждого адреса почты и каждого IP-адреса, поэтому через форму нельзя завалить чей-то ящик письмами.
  • Форма защищена Cloudflare Turnstile: он проверяет, что запрос отправляет человек, а не скрипт.
  • Форма отвечает одинаково, может адрес войти или нет, поэтому не выдаёт, у кого есть доступ.
  • Коды приходят с signin@teamonly.app, с настроенными SPF, DKIM и DMARC, чтобы почтовые сервисы могли проверить, что письмо действительно от нас.
  • Коды хранятся только в виде хеша, а доступ проверяется ещё раз при вводе кода, на случай если он за это время изменился.

Личность, которой приложение может доверять

Dispatch Worker удаляет все заголовки идентификации во входящем запросе и сам выставляет проверенные почту и имя, а также роли человека в этом приложении. SDK передаёт их приложению как ctx.user.

У двери
incoming request to expenses.acme.teamonly.appx-teamonly-user: ceo@acme.com    removed, not from usx-teamonly-user: dana@acme.com   added from verified sign-in
Названия заголовков условные. Приложение получает пользователя из SDK, а не из заголовков.

Отдельная база данных и хранилище файлов у каждого приложения

Каждое приложение получает свою базу данных D1, созданную при первой публикации, и свой префикс в хранилище R2 для файлов. Это единственные bindings к данным, подключённые к приложению. Приложения работают как отдельные скрипты в dispatch namespace Workers for Platforms и не могут добраться до bindings друг друга.

Мы проверяем это намеренно: приложение пытается прочитать данные, cookie или секреты другого приложения. Эти тесты запускались на рабочей платформе, и мы повторяем их каждый раз, когда меняются части, которые разделяют приложения.

План этажа с тремя комнатами: приложения expenses, inventory и lookup. В каждой комнате свои данные, файлы и секреты. Приложение expenses пытается заглянуть в комнату inventory и упирается в стену. Приложение inventory пытается заглянуть в комнату lookup и тоже упирается в стену. Данные, cookie и секреты остаются в своей комнате.

Каждое приложение в своей комнате. Данные, файлы, cookie и секреты остаются внутри.

Секреты хранятся отдельно от кода

Секреты задаются командой npx teamonly secrets set и хранятся как зашифрованные секреты Worker, привязанные к одному приложению. Их нет в репозитории, нет в сборке, и другие приложения их не видят. Файл навыка запрещает вашему ИИ вписывать ключи в код.

Cookie привязаны к адресу одного приложения

Сессионные cookie выставляются на точное имя хоста приложения, например expenses.acme.teamonly.app, с Secure, HttpOnly и SameSite. Они никогда не выставляются на teamonly.app или поддомен организации, поэтому одно приложение не может прочитать сессию другого.

Сейчас teamonly.app нет в Public Suffix List, поэтому браузеры считают все адреса приложений частью одного сайта, когда решают, что относится к same-site. Именно поэтому каждая сессионная cookie закреплена за точным адресом своего приложения. Если это изменится, мы напишем об этом здесь.

Код клиентов считается недоверенным

Мы исходим из того, что в любом приложении могут быть ошибки или вредоносный код: часть кода написал ИИ, а ошибиться может каждый. Поэтому аутентификация находится вне приложения, каждое приложение получает только свои ресурсы, и ничто из того, что отправляет приложение, не может изменить, кто вошёл. По той же причине приложение может отправлять письма только людям из вашей компании, не больше 100 получателей в час и 500 в день.

Где это работает

Всё работает на Cloudflare: Workers и Workers for Platforms для кода, D1 для баз данных, R2 для файлов и KV для сессий. Своих серверов у нас нет. Выбор региона данных входит в тариф Business, и мы настраиваем его вместе с вами при подключении.

Что настраиваем вместе с вами и чего не делаем никогда

В тарифе Business мы настраиваем Okta или другой SSO-провайдер вместе с вашим ИТ-отделом. Журнал аудита и регион данных настраиваются с вами так же. Они входят в тариф, но это не переключатели, которые вы включаете сами.

  • Настраиваем с вами
    Okta, OneLogin и другие SSO (тариф Business)
  • Настраиваем с вами
    Журнал аудита (тариф Business)
  • Настраиваем с вами
    Выбор региона данных (тариф Business)
  • Никогда
    Публичные приложения без входа

Сертификации SOC 2 или ISO у нас сейчас нет.

Как сообщить о проблеме безопасности

Напишите на hello@teamonly.dev со словом «Security» в теме. Каждое письмо читает человек, и мы ответим, чтобы подтвердить, что получили его. Пожалуйста, дайте нам разумное время на исправление, прежде чем рассказывать о проблеме публично.

Вопросы и ответы

Что обычно спрашивают

Все вопросы и ответы

Может ли одно приложение прочитать базу данных другого?

Нет. Каждое приложение получает доступ только к своей базе данных, своему хранилищу файлов и своим секретам. У него нет учётных данных и bindings к ресурсам других приложений, так что подключаться просто не к чему.

Может ли приложение выдать себя за другого пользователя?

Ни перед собой, ни перед нами. Заголовки идентификации во входящих запросах удаляются у двери и заменяются проверенными, поэтому посетитель не может притвориться кем-то другим. Личность, которую видит приложение, всегда берётся из настоящего входа.

Индексируют ли приложения поисковые системы?

Нет. Каждое приложение закрыто входом, поэтому поисковые роботы видят только экран входа, а каждый ответ, включая этот экран, отправляет noindex.

Ранний доступ

Запустите первый инструмент на этой неделе.

TeamOnly в раннем доступе, и все тарифы уже открыты. Попробуйте бесплатно 14 дней без карты или оформите подписку. В любом случае компания готова сразу, а три команды запускают первое приложение.

14 дней бесплатноИли оформите подписку сейчас

Для пробного периода карта не нужна. Дальше от $19 в месяц, отменить можно в любой момент.

Пока не готовы? Получайте новости

Укажите рабочую почту. Пишем редко и только когда есть что рассказать.

Вопросы пишите на hello@teamonly.dev, отвечает живой человек.