Supabase Auth 보안 체크리스트 2026: OAuth·JWT·MFA 10가지
Supabase Auth로 로그인 화면을 완성해도 출시 준비가 끝난 것은 아닙니다. 운영 Site URL이 localhost로 남아 있거나 OAuth callback 범위가 넓고, 서버가 JWT 서명을 확인하지 않거나 Auth와 데이터 권한을 같은 것으로 생각하면 계정 탈취와 다른 사용자 데이터 접근으로 이어질 수 있습니다.
먼저 확인할 인증 흐름 지도
| 단계 | 주요 위험 | 출시 전 증거 |
|---|---|---|
| 가입·로그인 | 약한 비밀번호, 봇·대량 요청 | 비밀번호 정책, CAPTCHA, 429 처리 |
| OAuth·이메일 링크 | 잘못된 redirect, Preview 혼용 | 운영 Site URL과 허용 경로 목록 |
| 서버 API | 서명 없는 JWT 신뢰, 비밀키 노출 | 검증된 claims와 서버 전용 키 |
| 데이터 접근 | 로그인 사용자 전체에 과도한 권한 | RLS와 계정 A/B 교차 테스트 |
| 세션 종료 | 분실 기기·탈취 세션 장기 유지 | 만료·재인증·로그아웃 시나리오 |
1. Site URL을 실제 운영 주소로 바꿨는가
Supabase Auth의 Site URL은 코드에서 redirectTo를 지정하지 않았을 때 쓰는 기본 목적지이며 이메일 확인과 비밀번호 재설정에도 중요합니다. 운영을 시작했는데 http://localhost:3000이 남아 있으면 사용자가 개발 주소로 이동하거나 복구 링크가 실패할 수 있습니다. HTTPS 정식 도메인과 실제 callback 경로를 기준으로 설정하세요.
Production, Preview, 로컬 주소를 한 목록에서 관리하되 운영 URL과 테스트 URL의 용도를 구분합니다. 배포 뒤 가입 확인, 비밀번호 재설정, Google·GitHub 로그인을 각각 실제 운영 도메인에서 끝까지 실행해 최종 주소를 기록하세요.
2. 운영 redirect URL은 정확한 경로만 허용했는가
Supabase는 Vercel Preview와 로컬 개발을 위해 와일드카드 패턴을 지원하지만 운영 환경에는 정확한 redirect URL 경로를 권장합니다. 필요 이상으로 넓은 https://** 형태를 운영 허용 목록에 두지 말고, 앱이 실제 사용하는 /auth/callback 같은 경로만 남기세요.
로그인 뒤 돌아갈 내부 경로를 next나 returnTo로 받는다면 외부 절대 URL을 그대로 허용하지 않습니다. 앱 내부 상대 경로인지 검사하고 허용 목록 밖이면 안전한 기본 화면으로 보내야 open redirect를 줄일 수 있습니다.
3. OAuth callback에서 코드 교환과 오류를 안전하게 처리하는가
서버 렌더링 앱은 callback에서 인증 코드를 세션으로 교환한 뒤 결과를 확인하고 이동해야 합니다. URL에 들어온 토큰·이메일·오류 설명을 로그에 통째로 남기거나 화면에 그대로 출력하지 마세요. 실패 시에는 사용자에게 다시 시도할 방법을 제공하되 내부 stack, provider 응답과 토큰 조각은 숨깁니다.
이메일 템플릿에서 사용자별 redirectTo를 쓴다면 Supabase 공식 안내처럼 SiteURL과 RedirectTo의 차이를 확인합니다. 가입 확인과 비밀번호 재설정 메일이 서로 다른 잘못된 주소로 가는 회귀 테스트도 필요합니다.
4. 서버가 JWT payload가 아니라 서명을 검증하는가
JWT의 가운데 payload는 Base64 URL로 읽을 수 있을 뿐, 읽었다고 신뢰할 수 있는 것은 아닙니다. 서버 API는 Supabase 클라이언트의 auth.getClaims() 또는 검증된 JWT 라이브러리와 프로젝트 JWKS를 사용해 서명, 만료 exp, 발급자 iss를 확인한 뒤 sub를 사용자 ID로 사용하세요.
브라우저가 보낸 user_id, 이메일이나 역할을 그대로 권한 근거로 삼지 않습니다. 검증된 토큰의 claims와 서버가 조회한 membership·role을 기준으로 판단해야 합니다. 자체 JWT 검증 코드를 만들 경우 키 회전과 JWKS 캐시 갱신까지 운영 절차에 포함해야 합니다.
5. publishable key와 서버 비밀키의 경계를 지켰는가
Supabase publishable key는 브라우저에서 프로젝트 API에 연결하기 위한 공개 식별자입니다. 공개된다고 즉시 사고는 아니지만 데이터 보호는 RLS에 의존합니다. 반대로 secret key, 레거시 service_role, 데이터베이스 비밀번호와 관리자 토큰은 RLS를 우회하거나 강한 권한을 가질 수 있으므로 서버 함수 밖으로 나오면 안 됩니다.
NEXT_PUBLIC_·VITE_ 변수, 빌드된 자바스크립트, 소스맵, API 오류와 배포 로그에서 비밀키 패턴을 검색하세요. 노출 이력이 있다면 코드에서 지우는 데 그치지 말고 발급 서비스에서 폐기·재발급하고 새 배포까지 완료합니다.
6. 비밀번호와 계정 변경 정책을 강화했는가
Supabase는 비밀번호를 bcrypt 해시로 저장하지만 제품이 허용하는 비밀번호 강도는 별도 설정입니다. 공식 문서는 최소 8자 미만을 권장하지 않으며 길이와 문자 조건을 설정할 수 있다고 안내합니다. 가능한 요금제에서는 유출된 비밀번호 차단도 검토하고, 비밀번호 관리자를 권장하세요.
비밀번호·이메일 변경처럼 계정 소유권에 영향을 주는 작업은 최근 로그인 여부와 재인증 흐름을 확인합니다. UI 버튼을 숨기는 방식이 아니라 Auth API 결과와 현재 세션 상태를 기준으로 실패를 처리해야 합니다.
7. CAPTCHA와 rate limit을 실제 화면에 연결했는가
Supabase Auth는 가입, 로그인과 비밀번호 재설정에 hCaptcha 또는 Cloudflare Turnstile을 연결할 수 있습니다. 대시보드에서 CAPTCHA를 켜기만 하고 프론트엔드가 받은 captchaToken을 Auth 요청에 전달하지 않으면 정상 사용자가 실패할 수 있으므로 성공·만료·재시도 흐름을 모두 테스트하세요.
Auth endpoint에는 기본 rate limit이 있으며 초과 시 429 응답이 발생합니다. 가입, OTP·magic link, 비밀번호 재설정 버튼을 연속 클릭했을 때 무한 요청을 보내지 말고 대기 시간을 안내합니다. 서버 프록시를 거친다면 모든 요청이 서버 한 IP로 보이지 않도록 Supabase 공식 IP 전달 조건도 확인하세요.
8. 관리자와 민감 기능에 MFA를 요구하는가
Supabase Auth는 authenticator 앱의 TOTP와 전화 메시지 기반 MFA를 지원합니다. 모든 사용자에게 같은 마찰을 주기보다 관리자 화면, 결제·환불, API 키 발급, 개인정보 내보내기처럼 피해가 큰 기능부터 MFA와 최근 재인증을 요구하세요.
MFA 등록만 구현하고 실제 권한 검사에서 보증 수준을 확인하지 않으면 우회될 수 있습니다. 등록, challenge, verify, 복구와 기기 분실 흐름을 테스트하고 RLS 또는 서버 함수가 필요한 인증 수준을 검증하는지 확인합니다.
9. 세션 수명과 로그아웃 보장을 제품 위험에 맞췄는가
Supabase 세션은 기본적으로 사용자가 로그아웃할 때까지 유지될 수 있고 여러 기기에서 동시에 활성화될 수 있습니다. 보안 민감 서비스는 요금제에서 제공하는 time-box, inactivity timeout, single-session 설정을 검토하세요. 설정 변경 효과는 다음 refresh 때 적용될 수 있고 JWT 만료 시간만큼 지연될 수 있으므로 즉시 종료된다고 가정하지 않습니다.
일반 기능에는 합리적인 access token 만료를 사용하고, 계정 삭제·결제 수단 변경처럼 즉시성이 필요한 작업은 검증된 session_id가 활성 세션인지 또는 최근 재인증했는지 추가로 확인할 수 있습니다. 토큰과 쿠키 전체를 분석 로그나 오류 수집 도구에 보내지 마세요.
10. Auth 성공과 데이터 권한을 두 계정으로 분리 검증했는가
계정 A와 B를 만들고 A의 데이터 ID를 B의 요청에 넣어 조회·수정·삭제를 각각 시도하세요. Auth는 B가 정상 사용자라는 사실만 보장하며 A의 데이터까지 볼 수 없게 만드는 것은 RLS와 서버 코드입니다. 조직형 앱은 초대 전, 일반 멤버, 관리자, 탈퇴 후 상태까지 역할별 표를 만드세요.
브라우저 UI에서 메뉴가 안 보이는지만 확인하지 말고 Network 요청을 재현해 HTTP 상태와 실제 데이터 변경을 확인합니다. Storage 파일과 서버 함수도 데이터베이스 테이블과 별도 권한 경계를 가질 수 있으므로 같은 계정 교차 테스트를 반복합니다.
출시 전 20분 실행 순서
- Auth URL Configuration에서 Site URL과 redirect 목록을 내보내지 않고 화면에서 대조합니다.
- 운영 가입 확인·비밀번호 재설정·OAuth 로그인을 각각 한 번 완료하고 최종 도메인을 기록합니다.
- 서버 API 하나에서 변조·만료 JWT를 보내 401 또는 403으로 거부되는지 확인합니다.
- 배포 자바스크립트와 로그에서
service_role, secret key와 토큰 패턴을 검색합니다. - 가입·재설정 버튼을 반복해 CAPTCHA 만료와 429 안내가 깨지지 않는지 봅니다.
- 계정 A/B로 서로의 데이터·파일·관리 API 접근을 교차 시도합니다.
- 로그아웃 뒤 일반 요청과 민감 작업을 다시 실행해 세션 종료 정책을 확인합니다.
공식 참고 자료
- Supabase Redirect URLs — Site URL, 허용 목록, 운영 경로와 Preview 패턴
- Supabase JSON Web Token — claims, 서명 검증과 JWKS
- Supabase Password Security — 길이·문자 조건, 유출 비밀번호와 재인증
- Supabase CAPTCHA Protection — hCaptcha·Turnstile과 captcha token 전달
- Supabase Auth Rate Limits — endpoint별 제한과 429 처리
- Supabase Multi-Factor Authentication — TOTP·전화 MFA와 보증 수준
- Supabase User Sessions — access·refresh token, 만료와 single session
- Supabase JWT Signing Keys — 비대칭 키, 회전·폐기와 JWKS 캐시
함께 볼 가이드
자주 묻는 질문
Supabase 로그인이 되면 사용자 데이터도 자동으로 안전한가요?
아닙니다. Auth는 사용자의 신원을 확인하지만 데이터 접근 권한은 RLS와 서버 권한 검사로 별도 제한해야 합니다. 두 테스트 계정으로 다른 사용자의 행과 파일에 접근할 수 없는지 확인하세요.
Supabase publishable key는 브라우저에 있어도 되나요?
publishable key는 클라이언트 사용을 전제로 하지만 그 자체가 데이터 권한을 막지는 않습니다. RLS가 필요하며 secret key, service_role, 데이터베이스 비밀번호는 브라우저에 두면 안 됩니다.
OAuth redirect URL에 와일드카드를 써도 되나요?
로컬 개발과 Preview 배포에는 제한된 패턴이 필요할 수 있지만, Supabase는 운영 환경에서 정확한 redirect URL 경로 사용을 권장합니다. Site URL도 localhost가 아니라 정식 운영 URL로 설정하세요.
로그아웃하면 발급된 JWT가 즉시 모든 서버에서 무효가 되나요?
항상 그런 것은 아닙니다. 짧은 수명의 access token은 만료 전까지 검증될 수 있으므로 민감한 작업은 JWT 검증뿐 아니라 session_id에 해당하는 활성 세션이나 재인증 상태를 추가로 확인할 수 있습니다.
공개 URL에서 키·번들·보안 헤더를 먼저 확인하고, 테스트 계정이 있다면 OAuth·세션·RLS 우회 시나리오를 별도로 재현하세요.
무료로 위험 확인