# Security checklist

> What to check in tokens, member integration signatures, channel data, the server API, and screen output before you launch a site with uchat.

Before you launch a site with uchat, check the items below. Each item explains what to do and why.

## At a glance {#checklist}

| Check | Item |
| --- | --- |
| ☐ | The chat room token and API key exist only in server code and server environment variables |
| ☐ | If you use member integration, time sync is turned on |
| ☐ | `auth` (permission level) is decided by the server from member data, never taken from the browser |
| ☐ | Data received on channels is validated, and server-sent data is checked with `auth === 4` |
| ☐ | User values put into `room.print` and skin areas are escaped |
| ☐ | Pages with a chat room are served over HTTPS |
| ☐ | No personal or payment information is sent through the server API or channels |
| ☐ | `user_data_url` is served from the same origin, without caching, and blocks loading from other sites |

## Tokens stay on the server {#tokens}

The **Token** (for member integration signatures) under **Chat room info** and the **API key** (for the server API) in the **API** menu of the admin page are as sensitive as passwords.

- If the token leaks, anyone can sign their own data and join with any nickname, even as an admin.
- If an API key leaks, anyone can send channel data as the server user (`auth` 4) and, depending on the key's permissions, read the user list and recent chats. If it leaks, revoke it or roll it right away in the **API** menu.
- Issue a separate API key for each purpose and turn on only the permissions it needs (Read, Events). Revoke keys you no longer use.
- Do not put them in JavaScript bundles, HTML, public repositories, or client apps. Keep them in server environment variables or a secret store.
- Create signatures on your server and send only the resulting string to the page. → [Member integration](/en/doc/2.1)

## Member integration signatures {#signature}

A member integration signature is an HMAC-SHA256 value computed with the chat room token over a string that holds each field separately. You cannot create a signature without the token, and a value signed for one field does not validate if moved to another field. However, there is no one-time value (nonce). → [Member integration signature spec](/en/doc/3.7)

- The same `user_data` string works as-is in another browser. Anyone who copies the page can join as that member.
- **Time sync** is what prevents reuse. When it is on, `user_data` more than 5 minutes older than the reference time is rejected, and the same `user_data` is accepted only for 5 minutes after its first use. When it is off, copied `user_data` can be reused at any time, so keep it on. → [Member integration](/en/doc/2.1#time-sync)
- Do not put pages containing `user_data` in a shared cache (CDN, proxy). The same signature could be delivered to another member.
- The server decides `auth` from permissions on your site. If you set `auth` from request parameters or cookie values, anyone can become an admin.

## Channel data can be forged {#channel-data}

Anyone in the chat room can use developer tools to send any data they want on a channel.

- Validate the number, types, and ranges of received values, and discard anything that does not match.
- Judge the sender by `msg.user.auth`, not by nickname. Data sent through the server API has `auth` 4 and no nickname.
- Saved channel values received on entry also arrive as `auth` 4. Moderators and above and the server API (`save`) can save, so `auth` 4 means "a value sent or saved by the server API, or a value saved by a moderator or above". If you must trust only the server API, use separate channel names for saving and for server data.
- For values whose outcome matters (draws, points, payment status), do not act on values sent by a browser. Decide them on your server. → [Sending and receiving real-time data](/en/doc/2.4)

## Values you put on the screen {#output}

- `room.print(html)` interprets its value as HTML. Escape values chosen by other people, such as nicknames, messages, and channel data.
- In skin UI API areas (`host`), insert text with `textContent`, and do not insert user values with `innerHTML`. → [Extending the skin UI](/en/doc/2.3)
- Hiding a menu only tidies the screen. The server checks permissions, and you should check permissions for your own features on your own server.

## HTTPS {#https}

Serve pages with a chat room over HTTPS. On an HTTP page, the chat connection opens over unencrypted `ws://`, so people on the same network can see messages and `user_data`. On an HTTPS page, it connects over `wss://`.

## Server API and personal data {#privacy}

For server API requests, the start of the body and the request IP are kept for 30 days in the call log of the **API** menu on the admin page. Channel data is delivered to the browsers of people connected to the chat room.

- Do not send personal data such as names, phone numbers, addresses, or card numbers, or payment information, through the server API or channels.
- Send only values that are safe to make public, such as display text or order numbers. → [Sending notifications from your server to a chat room](/en/doc/2.5)
- Call the server API only from your server, and never send requests containing the API key from a browser.

## user_data_url {#user-data-url}

The response from `user_data_url` runs as a `<script>` on your page. → [Member integration](/en/doc/2.1#user-data-url)

- Use a URL on the same origin as your site. If you run a script from another origin, that server can run anything on your page.
- Respond with `Cache-Control: no-store` so it is not cached. The signature must differ per member and per request.
- Other sites can also load the same URL as a `<script>` and obtain a signature using the visitor's login cookie. Reject `Sec-Fetch-Site: cross-site` requests, and set `SameSite=Lax` or stricter on login cookies.
- Put only the signed string in the response, never the token or other members' data.
