Files
Poker/README.md
T
agomizi1cmandClaude Opus 5.5 b2c91062f1 Initial commit: private Texas Hold'em ring game site (lobby + table stub)
- 6-digit access key gate with long-lived HttpOnly cookie, brute-force limits
- Lobby with saved player name and live table list
- Table creation (name, starting stack, blinds, blind timer)
- Table play screen stub (seats, button/SB/BB, phase, blinds, timer)
- Security hardening: strict CSP, origin checks, host pinning, input sanitizing

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-15 16:51:15 +09:00

141 lines
6.4 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Hold'em Night
友達専用のテキサスホールデム(リングゲーム)サイト。Node.js + Express + Socket.IO。
現状は「鍵入力 → ロビー → テーブル作成 → プレイ画面(スタブ)」まで。カード配布・ベッティングなどのゲーム処理は未実装で、プレイ画面ではホストが「次へ(仮)」でフェーズを手動で進められる。
## ローカルで動かす
```bash
cp .env.example .env # ACCESS_KEY を設定
npm install
npm start
```
http://localhost:4671 を開く。
## 設定(.env)
秘密情報はソースコードに書かず、プロジェクト直下の `.env`(git 管理外)か環境変数で渡す。`ACCESS_KEY` が無いとサーバは起動しない。
| 変数 | 既定値 | 内容 |
| --- | --- | --- |
| `ACCESS_KEY` | なし(必須) | 6桁の参加キー |
| `PORT` | `4671` | 待ち受けポート |
| `HOST` | `127.0.0.1` | 待ち受けアドレス。既定では外部から直接つながらない(nginx 経由で公開する) |
| `PUBLIC_ORIGIN` | なし | 本番の公開URL `https://poker.kanawo.net`。設定するとこのドメイン以外(IP直打ちや別ドメイン)へのアクセスと、他オリジンからの操作を拒否する。ローカル開発では設定しない |
| `SESSION_SECRET` | 自動生成 | Cookie 署名用。未指定なら `data/session-secret` に生成・保存 |
## セキュリティ
- **参加キー**: 正しいキーを入れたブラウザに HttpOnly / SameSite=Lax(HTTPS 時は Secure)の Cookie を保存。アクセスのたびに有効期限(400日)が延長されるので、以降は入力不要。
- **総当たり対策**: 同じIP(IPv6 は /64 単位)から30分に5回間違えるとロック。サーバ全体で1時間に30回間違えると新規の鍵入力を一時停止(入室済みの人は影響なし)。失敗はログに出る(`journalctl -u holdem`)。
- **入室状態の取り消し**: キーを変えるか、`data/session-secret` を削除して再起動すると、全員の Cookie が無効になる。
- **CSRF / Cross-Site WebSocket**: 状態を変える API と WebSocket 接続は Origin が自サイトのときだけ受け付ける。Socket.IO は WebSocket のみ。
- **XSS**: 厳格な CSP(インラインスクリプト・インラインスタイル禁止)、表示時のエスケープ、名前から制御文字・不可視文字を除去。
- **その他**: クリックジャッキング防止(`frame-ancestors 'none'`)、`nosniff`、`no-referrer`、HTML/API は `no-store`、検索エンジン除外、エラー時に内部情報を返さない、リクエストサイズ・イベント連打・テーブル数の上限。
- 他人の席を操作できないよう、席の持ち主を示すプレイヤーIDはブラウザ内だけに保存し、他のプレイヤーには送らない。
6桁のキーはそれ自体が強い秘密ではないので、上記の回数制限が守りの要になっている。キーは友達にだけ直接伝えること。
## 構成
| パス | 内容 |
| --- | --- |
| `server/index.js` | HTTP ルーティング・セキュリティヘッダ・REST API・Socket.IO |
| `server/auth.js` | 参加キー認証(Cookie)・回数制限・Origin チェック |
| `server/tables.js` | テーブル状態(席・ボタン・ブラインド・フェーズ・タイマー) |
| `server/config.js` | `.env` の読み込みと各種上限値 |
| `public/assets/blinds.js` | ブラインド計算(サーバ・ブラウザ共用) |
| `public/*.html`, `public/assets/*.js` | 各画面 |
- テーブルはメモリ上にのみ保持(サーバ再起動で消える)。誰も接続していない状態が30分続くと自動削除。
- 名前とプレイヤーID はブラウザの localStorage に保存。リロードしても同じ席に戻れる。
- 作成者がホスト(👑)。ホストが退席すると次の着席者に移る。
## VPS へのデプロイ例(Ubuntu + nginx + systemd + HTTPS)
公開URLは https://poker.kanawo.net 。HTTPS なしで公開するとキーと Cookie が平文で流れるので、必ず nginx + Let's Encrypt 経由で公開する。
事前に DNS で `poker.kanawo.net` の A レコード(IPv6 があれば AAAA も)を VPS の IP に向けておく。
```bash
# 専用ユーザーで動かす
sudo useradd --system --home /opt/holdem --shell /usr/sbin/nologin holdem
sudo git clone <このリポジトリ> /opt/holdem
cd /opt/holdem
sudo npm ci --omit=dev
# 秘密情報(本人のみ読める)
sudo cp .env.example .env
sudo nano .env # ACCESS_KEY=197107, PUBLIC_ORIGIN=https://poker.kanawo.net を設定
sudo mkdir -p data
sudo chown -R holdem:holdem .env data
sudo chmod 600 .env && sudo chmod 700 data
```
`/etc/systemd/system/holdem.service`:
```ini
[Unit]
Description=Hold'em Night
After=network.target
[Service]
User=holdem
WorkingDirectory=/opt/holdem
ExecStart=/usr/bin/node server/index.js
Environment=NODE_ENV=production
Restart=always
# サンドボックス
NoNewPrivileges=true
ProtectSystem=strict
ProtectHome=true
PrivateTmp=true
PrivateDevices=true
ReadWritePaths=/opt/holdem/data
CapabilityBoundingSet=
RestrictAddressFamilies=AF_INET AF_INET6 AF_UNIX
[Install]
WantedBy=multi-user.target
```
nginx(`/etc/nginx/sites-available/holdem`、WebSocket を通す):
```nginx
# poker.kanawo.net 以外のホスト名(IP直打ち等)は接続ごと切る
server {
listen 80 default_server;
listen [::]:80 default_server;
server_name _;
return 444;
}
server {
server_name poker.kanawo.net;
client_max_body_size 8k;
location / {
proxy_pass http://127.0.0.1:4671;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $http_host;
proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_read_timeout 1h;
}
}
```
```bash
sudo ln -s /etc/nginx/sites-available/holdem /etc/nginx/sites-enabled/
sudo certbot --nginx -d poker.kanawo.net # HTTPS 化(HTTP→HTTPS リダイレクトも設定される)
sudo systemctl daemon-reload && sudo systemctl enable --now holdem
# ファイアウォール: 4671 は開けない(nginx だけが 127.0.0.1 経由で使う)
sudo ufw allow OpenSSH && sudo ufw allow 'Nginx Full' && sudo ufw enable
```