Record 001 / 2026.09.30
Dice it Yourself
보안 테스트 및 수정 기록
인증, 콘텐츠 승인과 비공개 미디어·방 접근 권한의 로컬 회귀 검증.
목적과 범위
직접 개발·관리하는 Dice it Yourself의 권한 경계와 계정·콘텐츠 보호 동작을 확인했습니다. 개인 보안 학습의 일부로 기존 회귀 테스트를 실행하고, 확인 가능한 근거를 고정했습니다.
실제 서비스의 계정·업로드·데이터베이스를 사용하지 않았습니다. Google 제공자와 연결 전송 경계는 테스트 대역으로 대체하고, 인증 라이브러리의 실제 계정·세션·연결 정책과 로컬 애플리케이션 로직을 검사했습니다.
This record documents a focused local defensive validation of a game I develop and maintain. All 25 selected regression tests passed with synthetic fixtures. It includes account-linking policy, approved-content integrity, private media authorization, room membership, report integrity and connection denial. It is not a production penetration test or an independent security audit.
확인한 동작
| 항목 | 확인한 조건 | 근거 |
|---|---|---|
| Google 계정 연결 | 이메일이 같다는 이유만으로 기존 계정에 자동 연결하지 않습니다. 로그인한 사용자의 명시적 연결은 계정과 비밀번호를 보존합니다. | 통과auth-google-policy.test.ts |
| 인증 설정 | 개발용 loopback origin은 로컬 설정에서만 허용하고, Google 설정은 필요한 자격 증명이 모두 있을 때만 활성화합니다. | 통과auth-options.test.ts |
| 스킨 승인과 변조 | 승인은 심사한 정확한 콘텐츠에 연결됩니다. 설치 권한이나 공식 스킨 식별자만으로 변경본·철회본을 승인하지 않습니다. | 통과moderation.test.ts |
| 비공개 미디어와 전달 | 소유·설치·현재 방 참가 권한을 검사합니다. 추방·탈퇴·만료·승인 철회에 따라 접근과 전달 권한을 제한합니다. | 통과moderation.test.ts / media-access-index.test.ts |
| 방 참가 정보 | 참가·탈퇴·교체·삭제·rollback 이후 참가 정보가 일관됩니다. 오래된 참가 정보만으로 미디어 권한을 부여하지 않습니다. | 통과media-access-index.test.ts |
| 신고 기록 보존 | 신고 시점의 이름과 콘텐츠를 서버에서 고정하고, 이후 콘텐츠 변경·철회로 신고 원본이 바뀌지 않게 합니다. | 통과moderation.test.ts |
| 연결 거부와 수명 주기 | 방 입장이 거절되면 연결을 닫고 재시도를 멈춥니다. 계정 변경과 비공개 방 연결 수명 주기도 함께 검사합니다. | 통과public-room-access.test.ts / private-room-link.test.ts |
이 표는 테스트 기대 조건과 실행 결과를 요약합니다. 서비스 전체에 취약점이 없다는 판정은 아닙니다.
관련 구현 변경
아래는 저장소에 이미 존재하는 구현 변경입니다. 이번 실행은 해당 코드에 대한 재검증이며 추가 코드 수정은 하지 않았습니다.
인증·심사·비공개 미디어 권한 기반
인증 설정, 콘텐츠 심사 처리와 비공개 미디어 접근 제어, 관련 회귀 테스트를 추가·정리한 변경입니다.
75efad92572024641d017eebb656078432a86c85권한을 확인하는 피어 미디어 전달
미디어 접근 처리와 전달 권한 검사를 보완한 변경입니다. 현재 참가 상태와 수신자·만료 조건을 검증하는 경계를 포함합니다.
397fca107a6c5f6609c412747adfd80bb20681d1
기존 기능 추가·보완 이력입니다. 새로 발견하거나 공개한 취약점, CVE 또는 버그바운티 실적으로 분류하지 않습니다.
실행 근거
실행 명령과 테스트 25개
node node_modules/tsx/dist/cli.mjs --test --test-reporter=tap tests/auth-google-policy.test.ts tests/auth-options.test.ts tests/media-access-index.test.ts tests/moderation.test.ts tests/private-room-link.test.ts tests/public-room-access.test.ts- same-email Google sign-in cannot silently link; authenticated explicit link preserves user and password
- new Google users can sign up and explicit links cannot change an existing account email
- 3100 preview origins are accepted only when auth is configured for loopback development
- Google is absent unless both credentials are configured
- membership migration backfills and uses indexed participant/code lookup across unrelated rooms
- leave, rejoin, replacement, delete and rollback keep membership projection atomic
- gameplay-only writes do not rewrite membership rows; stale projection cannot grant media
- trusted private room batches reuse current membership without global media lookups
- canonical SHA256 ignores object key order but commits every media, text, setting and array edit
- submission stays pending; admin approval binds exact content; installer entitlement cannot authorize edits or revoked content
- official public eligibility needs exact current content, not officialId marker
- private media requires owner, acquired entitlement or active private-room reference; kick/leave/public room revoke peer access
- relay permission follows registered recipient, expiry and current membership
- moderator can view submitted media only; anonymous visibility follows live approval
- reports preserve server-resolved names/content and database trigger prevents later snapshot mutation
- workshop report snapshots approved content; later withdrawal does not alter it
- migration preserves legacy skin/media IDs, hides unreviewed listings and keeps installs/likes
- admin pages reach submissions and reports beyond 50 and preserve status filters
- solo rooms never start peer signaling; another human starts it lazily
- PeerJS channels wait for signaling registration before dialing
- incoming game negotiation survives a pulse before its data channel exists
- a private RoomLink never resumes HTTP polling, cursor relay or heartbeat after bootstrap
- account-change invalidation deduplicates notifications and detaches cleanly
- admission denied at link closes transport and stops retries
- admission denied at sync closes transport and stops retries
실행하지 않은 검사
- 운영·스테이징 환경의 HTTP 통합 검사 및 침투 테스트
- 실제 Google OAuth 제공자 연결과 실제 WebRTC 피어 협상
- 브라우저·모바일 화면과 사용자 조작 검사
- 독립 보안 감사와 전체 공격 표면에 대한 검증