# 보안 체크리스트

> 유챗을 붙인 사이트를 공개하기 전에 토큰, 회원 연동 서명, 채널 데이터, 서버 API, 화면 출력에서 확인할 항목을 정리해요.

유챗을 붙인 사이트를 공개하기 전에 아래 항목을 확인하세요. 각 항목은 무엇을 지켜야 하는지와 왜 그런지를 함께 적었어요.

## 한눈에 보기 {#checklist}

| 확인 | 항목 |
| --- | --- |
| ☐ | 채팅방 토큰과 API 키가 서버 코드·서버 환경 변수에만 있다 |
| ☐ | 회원 연동을 쓰면 시간 연동을 켜 두었다 |
| ☐ | `auth`(권한)는 서버가 회원 정보로 정하고, 브라우저 값을 받지 않는다 |
| ☐ | 채널로 받은 데이터를 검증하고, 서버 발신은 `auth === 4`로 확인한다 |
| ☐ | `room.print`와 스킨 영역에 넣는 사용자 값을 이스케이프한다 |
| ☐ | 채팅방이 있는 페이지를 HTTPS로 제공한다 |
| ☐ | 서버 API와 채널로 개인정보·결제 정보를 보내지 않는다 |
| ☐ | `user_data_url`은 같은 출처에서, 캐시 없이, 다른 사이트의 불러가기를 막아 제공한다 |

## 토큰은 서버에만 {#tokens}

관리 페이지 **채팅방 정보**의 **토큰**(회원 연동 서명용)과 **API** 메뉴의 **API 키**(서버 API용)는 비밀번호와 같아요.

- 토큰이 새면 누구나 원하는 닉네임과 관리자 권한으로 서명해 들어올 수 있어요.
- API 키가 새면 누구나 서버 사용자(`auth` 4)로 채널 데이터를 보내고, 키 권한에 따라 접속자 목록·최근 채팅을 읽을 수 있어요. 새면 **API** 메뉴에서 바로 폐기하거나 값을 다시 발급해요.
- API 키는 용도마다 따로 발급하고, 필요한 권한(조회·이벤트)만 켜요. 쓰지 않는 키는 폐기해요.
- 자바스크립트 번들, HTML, 공개 저장소, 클라이언트 앱에 넣지 마세요. 서버 환경 변수나 비밀 저장소에 둬요.
- 서명은 내 서버에서 만들고 결과 문자열만 페이지에 보내요. → [회원 연동](/doc/2.1)

## 회원 연동 서명 {#signature}

회원 연동 서명은 필드마다 따로 담은 문자열을 채팅방 토큰으로 HMAC-SHA256 계산한 값이에요. 토큰 없이는 서명을 만들 수 없고, 한 필드에 서명한 값을 다른 필드로 옮기면 서명이 맞지 않아요. 다만 한 번만 쓰게 하는 값(nonce)은 없어요. → [회원 연동 서명 규격](/doc/3.7)

- 같은 `user_data` 문자열은 다른 브라우저에서도 그대로 쓸 수 있어요. 페이지를 복사한 사람은 그 회원으로 들어올 수 있어요.
- 재사용을 막는 장치는 **시간 연동**이에요. 켜 두면 기준보다 5분 넘게 오래된 `user_data`를 거절하고, 같은 `user_data`는 처음 쓴 뒤 5분 동안만 받아요. 끄면 복사한 `user_data`를 언제든 다시 쓸 수 있으니 켜 두세요. → [회원 연동](/doc/2.1#time-sync)
- `user_data`가 든 페이지를 공용 캐시(CDN, 프록시)에 두지 마세요. 다른 회원에게 같은 서명이 전달될 수 있어요.
- `auth`는 서버가 내 사이트의 권한으로 정해요. 요청 파라미터나 쿠키 값으로 `auth`를 정하면 누구나 관리자가 될 수 있어요.

## 채널 데이터는 위조될 수 있다 {#channel-data}

채팅방에 있는 사람은 누구나 개발자 도구로 채널에 원하는 데이터를 보낼 수 있어요.

- 받은 데이터의 개수·타입·범위를 검증하고 맞지 않으면 버려요.
- 보낸 사람은 닉네임이 아니라 `msg.user.auth`로 판단해요. 서버 API로 보낸 데이터는 `auth`가 4이고 닉네임이 없어요.
- 입장할 때 받는 채널 저장 값도 `auth` 4로 와요. 저장은 부관리자 이상과 서버 API(`save`)가 할 수 있으므로, `auth` 4는 "서버 API가 보내거나 저장한 값, 또는 부관리자 이상이 저장한 값"이라는 뜻이에요. 서버 API만 믿어야 하면 저장 기능과 채널 이름을 나눠 쓰세요.
- 결과가 중요한 값(추첨, 포인트, 결제 상태)은 브라우저가 보낸 값을 근거로 처리하지 말고 내 서버에서 정하세요. → [실시간 데이터 주고받기](/doc/2.4)

## 화면에 넣는 값 {#output}

- `room.print(html)`은 값을 HTML로 해석해요. 닉네임, 메시지, 채널 데이터처럼 다른 사람이 정한 값은 이스케이프해서 넣어요.
- 스킨 UI API의 영역(`host`)에는 `textContent`로 글자를 넣고, 사용자 값을 `innerHTML`로 넣지 마세요. → [스킨 UI 확장하기](/doc/2.3)
- 메뉴를 숨기는 것은 화면 정리일 뿐이에요. 권한 검사는 서버가 하며, 내 기능의 권한도 내 서버에서 확인해요.

## HTTPS {#https}

채팅방이 있는 페이지를 HTTPS로 제공하세요. HTTP 페이지에서는 채팅 연결이 암호화되지 않은 `ws://`로 열려, 같은 네트워크에 있는 사람이 메시지와 `user_data`를 볼 수 있어요. HTTPS 페이지에서는 `wss://`로 연결해요.

## 서버 API와 개인정보 {#privacy}

서버 API 요청은 본문 앞부분과 요청 IP가 관리 페이지 **API** 메뉴의 호출 기록에 30일 동안 남아요. 채널 데이터는 채팅방에 접속한 사람의 브라우저로 전달돼요.

- 이름·전화번호·주소·카드 번호 같은 개인정보와 결제 정보는 서버 API와 채널로 보내지 마세요.
- 표시할 문구나 주문 번호처럼 공개되어도 되는 값만 보내요. → [서버에서 채팅방으로 알림 보내기](/doc/2.5)
- 서버 API는 내 서버에서만 부르고, API 키가 든 요청을 브라우저에서 보내지 마세요.

## user_data_url {#user-data-url}

`user_data_url`의 응답은 내 페이지에서 `<script>`로 실행돼요. → [회원 연동](/doc/2.1#user-data-url)

- 내 사이트와 같은 출처의 주소를 써요. 다른 출처의 스크립트를 실행하면 그 서버가 내 페이지에서 무엇이든 실행할 수 있어요.
- `Cache-Control: no-store`로 응답해 캐시되지 않게 해요. 회원마다, 요청마다 서명이 달라야 해요.
- 다른 사이트도 같은 주소를 `<script>`로 불러 방문자의 로그인 쿠키로 서명을 받아 갈 수 있어요. `Sec-Fetch-Site: cross-site` 요청을 거절하고, 로그인 쿠키에 `SameSite=Lax` 이상을 주세요.
- 응답에는 서명 문자열만 넣고 토큰이나 다른 회원 정보를 넣지 않아요.
