·기술 회고·3 분·리뷰 필요
적대적 보안 리뷰: 11개 확인 결함과 무엇을 지금 고쳤나
빌드 후 다중 에이전트 적대적 리뷰(보안/인증/데이터/캡처/PWA/스펙)를 돌려 11개 결함을 확인. 데모/스펙을 깨는 것 위주로 즉시 수정, 프로덕션 스케일 항목 2개는 문서화.
AI 버전
e1a924e로 앱이 동작한 뒤, 6개 차원(보안/인증/데이터/캡처/PWA/스펙)으로 적대적 리뷰를 돌리고 각 발견을 독립 검증 후에만 채택했다. 확인된 결함 11개. 전부 코드를 직접 읽어 검증.
즉시 수정 (모두 ed3600c)
| 결함 | 검증 | 수정 |
|---|---|---|
| dev-login 프로덕션 인증우회 | 라우트에 NODE_ENV 가드 없음 → 이메일만으로 타인 보관함 세션 발급 | 프로덕션 notFound() 가드 |
| 업로드 MIME 미검증 → 저장형 XSS | 기여자 file.type이 그대로 저장·반사, svg/html 가능 | MIME 화이트리스트(isAllowedImageMime/Audio) + 미디어 응답 X-Content-Type-Options: nosniff |
| SW가 비공개 보관함 HTML 캐시 | navigate 분기가 모든 응답 caches.put → 로그아웃/공용기기 유출 | network-only + 오프라인 폴백, 캐시 v1→v2로 기존 정리 |
| 미디어 라우트 Range 무시(iOS 재생 깨짐) | Accept-Ranges 광고하면서 항상 200 | Range 파싱 → 206 + Content-Range |
| CSRF 무방비 | 상태변경에 쿠키가 유일 권한, Origin 검사 없음 | proxy.ts(Next 16, 구 middleware)로 same-origin 강제 |
| rate limit 토큰별 없음 + 본문 무제한 버퍼 | 공개 엔드포인트 | 토큰별 throttle + 본문 Content-Length 조기 차단 |
| 부분 env가 세션비밀 재생성 | SESSION_SECRET만 줘도 무시되던 all-or-nothing | 키별 개별 적용 |
| AudioRecorder가 모든 실패를 '마이크 거부'로 | recordFailed 카피가 dead code(§18 위반) | err.name 구분 + 언마운트 안전 |
검증 방법(직접 실행): evil Origin POST → 403, same-origin → 200; text/html as photo → 400; curl -H "Range: bytes=0-99" → 206/content-range; 미인증 미디어 → 401.
지금 안 고치고 문서화 (프로덕션 스케일 한정, 데모 영향 없음)
- 알림 dedupe 영속화: 현재 인메모리(단일 프로세스 OK). 다중 인스턴스/재시작엔 중복 발송 가능 →
(reminderId, 날짜, HH:MM)영속 마커 필요. 현재 PGlite는 단일 인스턴스라 데모엔 무영향. - 신뢰 프록시 IP: rate-limit 키가
X-Forwarded-For원문 기반(스푸핑 가능) → 호스팅 신뢰 IP로. 토큰별 throttle로 부분 방어는 적용됨.
메모
- 리뷰는 발견 → 독립 검증 → 채택 구조. "그럴듯하지만 틀린" 발견을 거르려 검증 단계를 분리했다.
- 한 가지 정직한 한계: 위 수정들은 dev 서버 실행으로 검증했지만, 실제 공격 시나리오(예: 진짜 크로스사이트 폼)까지 재현하진 않았다 — Origin 검사·nosniff 같은 표준 방어를 코드로 확인한 수준.
리뷰 필요
내 시각이 아직 안 들어간 entry.