보안

앱을 비공개로, 서로 분리해 지키는 방법

만드는 사람이 직접 짜는 건 하나도 없어요. 로그인, 회사 사람만 접근하기, 앱마다 분리된 데이터, 검색 엔진에 노출되지 않기가 첫 배포 때 앱마다 함께 들어가요. 아래는 승인해야 하는 분을 위한 구체적인 설명이에요. TeamOnly는 얼리 액세스 중이에요. 이 페이지는 현재 어떻게 만들어지고 운영되는지를 설명하고, 바뀌면 정확하게 고쳐 둬요. 갖고 있지 않은 인증을 주장하지 않아요.

모든 요청은 먼저 로그인을 거쳐요

*.teamonly.app의 모든 앱 트래픽은 무엇보다 먼저 하나의 디스패치 Worker로 가요. 이 Worker가 호스트 이름에서 조직과 앱을 찾고, 유효한 세션이 있는지 확인한 다음에야 요청을 앱으로 넘겨요. 세션이 없으면 방문자에게는 로그인 화면이 보여요. 그 사람에게는 고객 코드가 절대 실행되지 않아요.

로그인 방법은 세 가지예요: Google OpenID Connect, Microsoft(Entra ID) OpenID Connect, 또는 회사 이메일 주소로 보내는 일회용 코드. Google과 Microsoft의 경우 ID 토큰을 저희 서버에서 서명, 대상(audience), 만료, 이메일 도메인까지 검증해요. Google의 hd 값은 요청에서 그대로 믿지 않고, 검증된 토큰에서 확인해요. Microsoft는 회사 및 학교 계정만 받고, 토큰 발급자(issuer)가 해당 테넌트와 일치해야 해요.

이메일 코드

Google이나 Microsoft 대신, 동료가 회사 주소로 6자리 코드를 요청할 수 있어요. 이메일이 Google Workspace나 Microsoft 365에 있지 않은 회사는 이 방법으로 따로 설정하지 않고 TeamOnly를 써요. 코드는 회사 도메인 중 하나의 메일함을 그 사람이 쓰고 있다는 걸 증명해요. Google이나 Microsoft 로그인이 증명하는 것과 같아요. 어떤 메일 서비스든 쓸 수 있어요. Gmail이나 Outlook.com 같은 개인 주소는 회사 앱에서 거부돼요.

  • 코드는 10분 뒤 만료돼요.
  • 5번 틀리면 코드가 더 이상 작동하지 않아요. 새 코드를 요청해야 해요.
  • 코드는 요청한 브라우저에서만 작동해요. 어깨너머로 본 코드나 전달받은 코드는 다른 브라우저에서 쓸 수 없어요.
  • 요청은 이메일 주소별, IP 주소별로 횟수가 제한돼서, 누구도 이 양식으로 메일함을 폭주시킬 수 없어요.
  • 양식은 Cloudflare Turnstile로 보호돼요. 스크립트가 아니라 사람이 요청하는지 확인해요.
  • 양식은 주소가 로그인할 수 있든 없든 같은 답을 보여 줘서, 누가 접근 권한이 있는지 드러내지 않아요.
  • 코드는 signin@teamonly.app에서 보내고, SPF, DKIM, DMARC가 설정돼 있어서 메일 서비스가 정말 저희가 보낸 건지 확인할 수 있어요.
  • 코드는 해시로만 저장되고, 그사이 바뀌었을 수 있으니 코드를 입력할 때 접근 권한을 다시 확인해요.

앱이 믿을 수 있는 신원

디스패치 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 저장소 프리픽스를 받아요. 앱에 연결된 데이터 바인딩은 이것뿐이에요. 앱은 Workers for Platforms 디스패치 네임스페이스에서 별도 스크립트로 실행되고, 서로의 바인딩에 닿을 수 없어요.

저희는 이걸 일부러 테스트해요: 한 앱이 다른 앱의 데이터, 쿠키, 시크릿을 읽으려는 시도. 이 테스트는 실제 운영 중인 플랫폼에서 실행했고, 앱을 분리하는 부분이 바뀔 때마다 다시 실행해요.

방 세 개가 있는 평면도: expenses, inventory, lookup 앱이에요. 방마다 자기 데이터, 파일, 시크릿을 갖고 있어요. expenses 앱이 inventory 방으로 손을 뻗다가 벽에 막혀요. inventory 앱도 lookup 방으로 손을 뻗다가 벽에 막혀요. 데이터, 쿠키, 시크릿은 각자의 방에 그대로 있어요.

앱마다 각자의 방에 있어요. 데이터, 파일, 쿠키, 시크릿은 그 안에 머물러요.

시크릿은 코드 밖에 저장돼요

시크릿은 npx teamonly secrets set으로 설정하고, 그 앱 하나에 연결된 암호화된 Worker 시크릿으로 저장돼요. 저장소에도, 번들에도 없고, 다른 앱에서도 보이지 않아요. 스킬 파일은 AI에게 키를 절대 코드에 넣지 말라고 알려 줘요.

앱 하나의 주소로 범위를 정한 쿠키

세션 쿠키는 expenses.acme.teamonly.app처럼 정확한 앱 호스트 이름에 Secure, HttpOnly, SameSite와 함께 설정돼요. teamonly.app이나 조직의 서브도메인에는 절대 설정되지 않아서, 한 앱이 다른 앱의 세션을 읽을 수 없어요.

teamonly.app은 현재 Public Suffix List에 없어서, 브라우저는 same-site 여부를 판단할 때 모든 앱 주소를 하나의 사이트로 봐요. 그래서 세션 쿠키를 각 앱의 정확한 주소에 고정해요. 이 상황이 바뀌면 이 페이지에 알려 드릴게요.

고객 코드는 신뢰할 수 없는 코드로 다뤄요

어떤 앱이든 실수나 악의적인 코드가 있을 수 있다고 가정해요. 일부는 AI가 썼고, 누구나 실수할 수 있으니까요. 그래서 인증은 앱 밖에 있고, 각 앱은 자기 리소스만 받고, 앱이 무엇을 보내든 누가 로그인했는지는 바뀌지 않아요. 앱이 회사 사람에게만, 시간당 수신자 100명과 하루 500명까지만 이메일을 보낼 수 있는 것도 같은 이유예요.

어디서 실행되나요

모든 게 Cloudflare에서 실행돼요: 코드는 Workers와 Workers for Platforms, 데이터베이스는 D1, 파일은 R2, 세션은 KV. 저희 서버는 따로 운영하지 않아요. 데이터 리전 선택은 Business 요금제에 포함되고, 가입할 때 함께 설정해 드려요.

함께 설정하는 것, 절대 하지 않는 것

Business 요금제에서는 IT 팀과 함께 Okta나 다른 SSO 제공자를 설정해요. 감사 로그와 데이터 리전도 같은 방식으로 함께 구성해요. 요금제에 포함된 항목이지, 직접 켜는 스위치가 아니에요.

  • 함께 설정
    Okta, OneLogin 등 SSO(Business 요금제)
  • 함께 설정
    감사 로그(Business 요금제)
  • 함께 설정
    데이터 리전 선택(Business 요금제)
  • 절대 안 함
    로그인 없이 열리는 공개 앱

현재 SOC 2나 ISO 인증은 없어요.

보안 문제 신고

제목에 “Security”를 넣어 hello@teamonly.dev로 메일을 보내 주세요. 모든 메일을 사람이 읽고, 받았다고 답장해 드려요. 문제를 공개하기 전에 저희가 고칠 수 있도록 적당한 시간을 주세요.

FAQ

자주 묻는 질문

모든 질문과 답변

한 앱이 다른 앱의 데이터베이스를 읽을 수 있나요?

아니요. 각 앱은 자기 데이터베이스, 파일 저장소, 시크릿에만 접근할 수 있어요. 다른 앱의 리소스에 대한 자격 증명이나 바인딩이 없으니 연결할 대상 자체가 없어요.

앱이 다른 사용자인 척할 수 있나요?

앱 자신에게도, 저희에게도 안 돼요. 들어오는 요청의 신원 헤더는 문에서 제거되고 검증된 헤더로 바뀌어서, 방문자가 다른 사람인 척할 수 없어요. 앱이 보는 신원은 항상 실제 로그인에서 나와요.

앱이 검색 엔진에 색인되나요?

아니요. 모든 앱이 로그인 뒤에 있어서 크롤러는 로그인 화면까지만 닿고, 그 화면을 포함한 모든 응답이 noindex를 보내요.

얼리 액세스

이번 주에 첫 툴을 올려 보세요.

TeamOnly는 얼리 액세스 중이고, 지금 모든 요금제에 가입할 수 있어요. 카드 없이 14일 동안 무료로 써 보거나, 구독하세요. 어느 쪽이든 회사 설정이 바로 끝나고, 명령어 세 개면 첫 앱이 온라인에 올라가요.

14일 무료 체험하기아니면 지금 구독하기

무료 체험에는 카드가 필요 없어요. 그 뒤로 월 $19부터, 언제든 해지할 수 있어요.

아직 준비가 안 됐나요? 소식 받기

회사 이메일을 써 주세요. 메일은 가끔, 보낼 만한 소식이 있을 때만 보내요.

질문은 hello@teamonly.dev로 보내 주세요. 사람이 직접 답해요.