コンテンツにスキップ

セキュリティ

考え方

  • 確認はすべてサーバで行う。アプリから送られてくるユーザー ID・時刻・ロール・権限は信用しません。
  • 公開の新規登録はありません。アカウントは管理者が作るか、招待リンクで作ります。
  • 本番では TLS(HTTPS)が必須です。データベースとオブジェクトストレージのポートは外に出しません。

認証

項目 内容
パスワード Argon2id でハッシュにして保存(平文は保存しません)。既定は 8 文字以上
ログインの試行 IP ごと・アカウントごとに回数を制限。ユーザー名が存在するかどうかで応答を変えません
アクセストークン 15 分で切れる短いトークン。毎回セッションが有効かを確かめるので、失効はすぐに効きます
リフレッシュトークン 使うたびに新しくなります(ローテーション)。古いトークンが使われたら盗難の疑いとしてセッションを失効させます。サーバにはハッシュだけを保存
セッション 1 端末 = 1 セッション。設定から自分のログイン中の端末を見て、失効させられます
2 要素認証 認証アプリ(TOTP)と回復コード
Google でログイン OpenID Connect + PKCE。Google とやり取りするのはサーバだけで、許可したドメインのアカウントだけを受け付けます

アクセストークンとリフレッシュトークンの使い方は次のとおりです。

sequenceDiagram
  participant A as アプリ
  participant S as サーバ
  A->>S: ログイン(パスワード、2 要素認証のコード)
  S-->>A: アクセストークン(15 分)<br>リフレッシュトークン
  A->>S: API・WebSocket(アクセストークン)
  Note over S: 毎回セッションが<br>有効かを確かめる
  A->>S: 期限の前にリフレッシュ
  S-->>A: 新しい 2 つのトークン<br>(古いリフレッシュトークンは使えなくなる)
  Note over A,S: 古いリフレッシュトークンがもう一度使われたら、<br>盗難の疑いとしてセッションを失効させる

端末に保存する秘密は、iOS は Keychain、Android は Android Keystore の鍵で暗号化した保存先、デスクトップ版は OS の 資格情報ストアに置きます。ブラウザ版のリフレッシュトークンは、スクリプトから読めない cookie(HttpOnly)に入れます。

権限

  • システムのロールは 管理者・メンバー・ゲスト の 3 つ、チャンネルの中のロールは オーナー・メンバー です。
  • 非公開チャンネルと DM は、メンバーだけが読めます。管理者でも、メンバーでなければアプリから読めません。
  • ゲストは入れてもらったチャンネルの中だけで動け、同じチャンネルの人しか見えません。
  • 管理の操作は監査ログに残ります。

添付ファイル

  • アップロードもダウンロードもログインが必要です。オブジェクトストレージは非公開で、アプリが直接触れることはありません。
  • ファイルの種類は、アプリの申告ではなく、ファイルの先頭のバイトから判定します。
  • ダウンロードできるのは、そのチャンネルのメンバーだけです。メッセージを消すと、添付ファイルもすぐに見られなくなります。
  • 画像は展開する前に大きさを確かめ、サムネイルからは EXIF(位置情報など)を取り除きます。
  • SVG・HTML などは、ブラウザの中で開かず、常にダウンロードとして渡します。
  • 文書のプレビューを作る converter は、インターネットにもホストにも出られないネットワークに閉じ込めています。

リンクプレビュー

URL のプレビューはサーバが取りに行くので、内部のネットワークへの不正なアクセス(SSRF)を防ぐ検査をしています (リダイレクトのたびの確認、大きさと時間の上限)。

秘密の情報

APNs の鍵・API キー・パスワード・トークンは、リポジトリに入れません。infra/.env と infra/secrets/ に置き、 サーバにだけ保管します。ログにも、トークン・パスワード・メッセージの本文は出しません。

サーバの担当者に見えるもの

メッセージはデータベースの中で暗号化していません。アプリの権限とは関係なく、サーバ・データベース・ バックアップを扱える人はすべてのメッセージを読めます(プライバシーとデータの扱い)。 バックアップの保存先の暗号化は、ホスト側で行ってください。

脆弱性の報告

脆弱性を見つけたら、公開の Issue ではなく、SECURITY.md の 方法で非公開で知らせてください。

詳しくは docs/SECURITY.md をご覧ください。