# セキュリティチェックリスト

> uchatを組み込んだサイトを公開する前に、トークン、会員連携の署名、チャンネルデータ、サーバーAPI、画面出力で確認すべき項目をまとめています。

uchatを組み込んだサイトを公開する前に、以下の項目を確認してください。各項目には、何を守るべきかとその理由をあわせて記載しています。

## 一覧 {#checklist}

| 確認 | 項目 |
| --- | --- |
| ☐ | チャットルームのトークンとAPIキーがサーバーのコード・サーバーの環境変数にだけある |
| ☐ | 会員連携を使う場合、時刻連携をオンにしている |
| ☐ | `auth`（権限）はサーバーが会員情報から決め、ブラウザの値を受け取らない |
| ☐ | チャンネルで受け取ったデータを検証し、サーバーからの送信は `auth === 4` で確認している |
| ☐ | `room.print` やスキン領域に入れるユーザーの値をエスケープしている |
| ☐ | チャットルームのあるページをHTTPSで提供している |
| ☐ | サーバーAPIとチャンネルで個人情報・決済情報を送らない |
| ☐ | `user_data_url` は同じオリジンから、キャッシュなしで、他サイトからの読み込みを防いで提供している |

## トークンはサーバーだけに {#tokens}

管理ページの**チャットルーム情報**にある**トークン**（会員連携の署名用）と、**API** メニューの**APIキー**（サーバーAPI用）は、パスワードと同じです。

- トークンが漏れると、誰でも好きなニックネームと管理者権限で署名して入室できます。
- APIキーが漏れると、誰でもサーバーユーザー（`auth` 4）としてチャンネルデータを送れ、キーの権限によっては接続者一覧・最近のチャットを読み取れます。漏れたら、**API** メニューですぐに無効化するか、値を再発行します。
- APIキーは用途ごとに別々に発行し、必要な権限（取得・イベント）だけをオンにします。使っていないキーは無効化します。
- JavaScriptバンドル、HTML、公開リポジトリ、クライアントアプリに入れないでください。サーバーの環境変数やシークレットストアに置きます。
- 署名は自分のサーバーで作り、結果の文字列だけをページに送ります。→ [会員連携](/ja/doc/2.1)

## 会員連携の署名 {#signature}

会員連携の署名は、フィールドごとに分けて格納した文字列を、チャットルームのトークンでHMAC-SHA256計算した値です。トークンがなければ署名は作れず、あるフィールドに署名した値を別のフィールドに移すと署名が一致しません。ただし、一度だけ使わせる値（nonce）はありません。→ [会員連携の署名仕様](/ja/doc/3.7)

- 同じ `user_data` 文字列は、別のブラウザでもそのまま使えます。ページをコピーした人は、その会員として入室できます。
- 再利用を防ぐ仕組みは**時刻連携**です。オンにすると、基準より5分を超えて古い `user_data` を拒否し、同じ `user_data` は最初に使ってから5分間だけ受け付けます。オフにするとコピーした `user_data` をいつでも再利用できるため、オンにしておいてください。→ [会員連携](/ja/doc/2.1#time-sync)
- `user_data` を含むページを共有キャッシュ（CDN、プロキシ）に置かないでください。ほかの会員に同じ署名が渡る可能性があります。
- `auth` は、サーバーが自分のサイトの権限から決めます。リクエストパラメーターやCookieの値で `auth` を決めると、誰でも管理者になれてしまいます。

## チャンネルデータは偽装できる {#channel-data}

チャットルームにいる人は誰でも、開発者ツールでチャンネルに好きなデータを送れます。

- 受け取ったデータの数・型・範囲を検証し、合わないものは捨てます。
- 送信者はニックネームではなく `msg.user.auth` で判断します。サーバーAPIで送られたデータは `auth` が4で、ニックネームがありません。
- 入室時に受け取るチャンネルの保存値も `auth` 4で届きます。保存はモデレーター以上とサーバーAPI（`save`）ができるため、`auth` 4は「サーバーAPIが送った・保存した値、またはモデレーター以上が保存した値」という意味です。サーバーAPIだけを信用する必要がある場合は、保存機能とチャンネル名を分けて使ってください。
- 結果が重要な値（抽選、ポイント、決済状態）は、ブラウザが送った値を根拠に処理せず、自分のサーバーで決めてください。→ [リアルタイムデータの送受信](/ja/doc/2.4)

## 画面に入れる値 {#output}

- `room.print(html)` は値をHTMLとして解釈します。ニックネーム、メッセージ、チャンネルデータのように他人が決めた値はエスケープしてから入れます。
- スキンUI APIの領域（`host`）には `textContent` で文字を入れ、ユーザーの値を `innerHTML` で入れないでください。→ [スキンUIを拡張する](/ja/doc/2.3)
- メニューを隠すのは画面の整理にすぎません。権限チェックはサーバーが行い、自分の機能の権限も自分のサーバーで確認します。

## HTTPS {#https}

チャットルームのあるページはHTTPSで提供してください。HTTPのページではチャット接続が暗号化されていない `ws://` で開かれ、同じネットワークにいる人がメッセージや `user_data` を見られます。HTTPSのページでは `wss://` で接続します。

## サーバーAPIと個人情報 {#privacy}

サーバーAPIのリクエストは、本文の先頭部分とリクエストIPが管理ページの **API** メニューの呼び出し履歴に30日間残ります。チャンネルデータは、チャットルームに接続している人のブラウザに届けられます。

- 氏名・電話番号・住所・カード番号などの個人情報や決済情報は、サーバーAPIやチャンネルで送らないでください。
- 表示する文言や注文番号のように、公開されても問題ない値だけを送ります。→ [サーバーからチャットルームに通知を送る](/ja/doc/2.5)
- サーバーAPIは自分のサーバーからだけ呼び、APIキーを含むリクエストをブラウザから送らないでください。

## user_data_url {#user-data-url}

`user_data_url` のレスポンスは、自分のページで `<script>` として実行されます。→ [会員連携](/ja/doc/2.1#user-data-url)

- 自分のサイトと同じオリジンのURLを使います。別オリジンのスクリプトを実行すると、そのサーバーが自分のページで何でも実行できてしまいます。
- `Cache-Control: no-store` で応答し、キャッシュされないようにします。署名は会員ごと、リクエストごとに異なる必要があります。
- ほかのサイトも同じURLを `<script>` で読み込み、訪問者のログインCookieで署名を取得できます。`Sec-Fetch-Site: cross-site` のリクエストを拒否し、ログインCookieには `SameSite=Lax` 以上を設定してください。
- レスポンスには署名文字列だけを入れ、トークンやほかの会員の情報は入れません。
