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

6.4 KiB
Raw Blame History

Hold'em Night

友達専用のテキサスホールデム(リングゲーム)サイト。Node.js + Express + Socket.IO。

現状は「鍵入力 → ロビー → テーブル作成 → プレイ画面(スタブ)」まで。カード配布・ベッティングなどのゲーム処理は未実装で、プレイ画面ではホストが「次へ(仮)」でフェーズを手動で進められる。

ローカルで動かす

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 に向けておく。

# 専用ユーザーで動かす
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:

[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 を通す):

# 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;
    }
}
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