# 보안

## 인증

이메일과 비밀번호. 비밀번호는 해시만 저장합니다. 로그인 성공과 실패 뒤 세션 ID를 바꿉니다. 로그인 시도는 분당 5회를 넘기면 잠시 막습니다. 이메일 인증 전에는 피드백과 발행을 받지 않습니다. 관리자 TOTP는 RFC 6238이며 비밀키는 `users.two_factor_secret`에 있습니다.

정지·탈퇴 계정은 요청 중에 로그아웃됩니다.

## 권한

증거 이미지는 작성자와 모더레이터·관리자만 봅니다. 앱 제작자는 기본으로 볼 수 없습니다. 운영자 열람은 `evidence_access_logs`에 남고, IP는 앱 키와 함께 해시합니다. 해시는 완전한 익명화가 아닙니다.

스토어로 나가는 링크는 저장된 HTTPS 주소만 사용합니다. 알림의 이동 주소는 같은 사이트 경로만 허용합니다.

API의 변경 요청은 `Idempotency-Key`가 있으면 본문 해시가 다를 때 409를 반환합니다.

## 업로드

용량을 먼저 보고, SVG·HTML·PHP와 이미지가 아닌 파일을 거절합니다. 통과한 파일은 다시 인코딩해 EXIF를 벗기고 픽셀 수를 제한합니다. 증거 디스크는 `serve`가 꺼져 있습니다.

## HTTP

`Content-Security-Policy`, `X-Content-Type-Options: nosniff`, `X-Frame-Options: DENY`, Referrer-Policy를 붙입니다. `vibepeer.app`의 HTTP 요청과 `www` 호스트는 HTTPS 정본으로 308합니다. HSTS는 `SECURITY_HSTS=true`일 때만 보냅니다.

비운영 환경 페이지는 `noindex`입니다. canonical 정본은 운영에서만 `https://vibepeer.app`입니다.

## 운영 비밀

`.env`, 앱 키, 메일 비밀번호, Turnstile, 2FA 비밀은 커밋하지 않습니다. 관리자 비밀번호를 코드나 시드에 넣지 않습니다. `php artisan vibepeer:make-admin`만 사용합니다.

취약점을 발견하면 공개 이슈 대신 운영 이메일로 먼저 알려 주세요. 연락 주소는 도메인 메일함이 준비된 뒤 `security@vibepeer.app`를 기준으로 합니다. 그 메일함 개통 여부는 이 저장소가 확인하지 않습니다.
