// docs / baas security / clerk hardening
Clerk 보안 체크리스트: 20개 항목
Clerk는 앱의 인증, 세션, 조직을 처리합니다 — 즉, 잘못 구성된 Clerk 통합은 인증 우회, 세션 고정 벡터, 또는 조직 유출 경로입니다. 이 체크리스트는 키, 세션 설정, 웹훅, 조직, JWT 템플릿, 지속적인 모니터링에 걸친 20개 항목 감사입니다. AI 코딩 도구는 합리적인 기본값으로 Clerk를 빠르게 연결합니다. 이 목록은 그들이 테이블에 남기는 항목을 잡아냅니다.
인증 계층 설정 오류가 AI 도구의 약점인 이유에 대한 배경은 AI 코딩 도구가 보안 격차를 남기는 이유를 참조하세요. Auth0의 병렬 체크리스트는 Auth0 보안 체크리스트를 참조하세요.
환경 키 및 오리진 허용 목록
Clerk는 프로젝트당 두 개의 별개 키를 발급합니다. 이를 혼합하거나 유출하는 것이 첫 번째 실패 모드입니다.
- 브라우저에서는 게시 가능 키(프로덕션의 경우
pk_live_*, 개발의 경우pk_test_*)를 사용하고, 서버에서만 비밀 키(sk_live_*/sk_test_*)를 사용하세요. 게시 가능 키는NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY에서 안전합니다. 비밀 키는 결코 공개 env 접두사를 가져서는 안 되며 결코 클라이언트 컴포넌트에 나타나서는 안 됩니다. - 프로덕션 앱이
pk_test_*가 아닌pk_live_*를 사용하는지 확인. 테스트 인스턴스는 미확인 이메일 주소와 비활성화된 MFA를 허용합니다 — 프로덕션에 테스트 모드를 출시하는 것은 인증 우회입니다. - Clerk 대시보드에서 허용된 오리진 구성. Settings → Domains → Allowed origins은 프로덕션 도메인을 정확히 나열해야 합니다. 빈 또는 와일드카드 오리진 목록은 공격자가 백엔드와 통신하는 가짜 Clerk 프론트엔드를 만들 수 있게 합니다.
- 퇴직이나 의심되는 유출 시 비밀 키 교체. 대시보드 → API Keys → Reset. 이전 키가 무효화됩니다. 교체 전에 새 값으로 서버 측 코드를 재배포하세요.
세션 구성
세션 만료 및 유휴 시간 초과는 도난당한 세션이 10분 사건인지 30일 사건인지의 차이입니다.
- 민감한 데이터를 처리하는 SaaS 앱의 경우 세션 비활성 시간 초과를 30분 이하로 설정. 대시보드 → Sessions → Inactivity timeout. 은행 등급 앱은 5-10분, 표준 SaaS는 30-60분, 소비자 앱은 1-7일을 사용해야 합니다. 기본값은 7일입니다.
- 비밀번호 변경, 이메일 변경, MFA 등록 시 세션 해지 활성화. 대시보드 → Sessions → Revoke on. 이들은 사용자가 시작한 보안 이벤트입니다. 다른 기기의 기존 세션은 종료되어야 합니다.
- 로그인 시뿐만 아니라 모든 보호된 경로에서 서버 측 세션 확인. Next.js에서: 서버 컴포넌트 / API 경로의
const { userId } = await auth();은 쿠키에서 JWT를 읽고 검증합니다. 쿠키만 확인하는 것을 결코 신뢰하지 마세요. - 세션 쿠키에
SameSite=Lax(기본값) 또는Strict설정. DevTools → Application → Cookies에서 확인하세요.SameSite=None은 CSRF 벡터입니다 — 크로스 도메인 인증 설정을 명시적으로 구성하지 않은 한 결코 사용하지 마세요.
웹훅 검증
Clerk 웹훅은 사용자 생명주기 이벤트(생성, 업데이트, 삭제, session.ended)에서 발생합니다. 이들은 데이터베이스의 동기화 메커니즘이며 — 위조된 웹훅은 데이터베이스 쓰기 프리미티브입니다.
- 모든 웹훅에서 Svix 서명 검증. Clerk 웹훅은 Svix로 서명됩니다.
new Webhook(secret).verify(body, headers)를 사용하세요. 검증 실패 시401로 거부하세요. - 웹훅 비밀을 환경 변수에 저장하고 코드에는 결코 저장하지 마세요. 비밀은 매 대시보드 재생성마다 교체됩니다 — 배포는 상수가 아닌 env에서 읽어야 합니다.
- 모든 핸들러에서 멱등성. 웹훅 전달은 반복될 수 있습니다. 중복 제거를 위해
webhook_events테이블에서svix-id헤더를 기본 키로 사용하세요. 상태 변경과 멱등성 삽입을 동일한 트랜잭션으로 감싸세요. user.deleted에서 24시간 이내에 PII 하드 삭제 또는 익명화. GDPR / CCPA가 이를 요구합니다. 삭제 경로 감사: 어떤 테이블이 이 사용자의 데이터를 가지고 있나요? 가능한 곳에서 FK ON DELETE CASCADE를 사용하세요.
조직 및 권한
Clerk Organizations를 사용하면 조직 경계가 테넌트 격리입니다. 모든 서버 측 쿼리는 이를 기준으로 필터링해야 합니다.
- 모든 API 경로에서
auth()에서userId와orgId를 모두 읽고 두 가지로 데이터베이스 쿼리를 필터링합니다.WHERE org_id = $orgId AND user_id = $userId. 요청 본문의org_id를 결코 신뢰하지 마세요. - <strong>Use Clerk role checks for privileged operations, not boolean checks against the user object.</strong> <code>has({ role: 'org:admin' })</code> reads the role from the verified JWT. A user can spoof a boolean on a stale client object; they cannot spoof a JWT claim.
- 두 개의 실제 조직 계정으로 조직 간 격리 테스트. 조직 A를 만들고 데이터를 채운 후 다른 브라우저에서 조직 B로 로그인하고 API를 통해 조직 A의 데이터를 읽으려고 시도하세요. 응답은
403또는404여야 합니다.
JWT 템플릿 및 외부 통합
JWT 템플릿은 Clerk 신원을 Supabase, Firebase, 기타 다운스트림 서비스로 푸시합니다. 잘못 구성된 템플릿은 클레임을 과도하게 공유하거나 의도하지 않은 데이터를 노출합니다.
- 각 JWT 템플릿에 대해 모든 클레임을 나열하고 필요한지 확인. 대시보드 → JWT Templates. Supabase로
email과phone을 발송하는 템플릿은 브라우저에서 JWT를 읽는 누구에게나 PII를 노출합니다. - 클라이언트 측 다운스트림 호출에 사용되는 JWT 템플릿에 짧은 만료 설정. 다운스트림 API 요청의 경우 60초가 표준입니다. 더 긴 수명의 JWT는 도난당하고 재생됩니다.
- 수신 측에서 청중(
aud) 클레임 확인. Supabase, Firebase 등은aud가 예상되는 서비스 식별자와 일치하는지 확인해야 합니다. 이것 없이는 서비스 A를 위해 발급된 JWT가 서비스 B에 인증할 수 있습니다.
운영 모니터링
인증은 당신이 가진 가장 신호가 강한 로그 소스입니다. 그것을 모니터링하세요.
- IP당 / 계정당 로그인 실패 급증 경보. 정상 실패율의 50배는 자격 증명 스터핑 공격입니다. Clerk는 이러한 이벤트를 웹훅으로 내보냅니다 — SIEM으로 라우팅하세요.
- 세션 및 인스턴스 설정 드리프트의 분기별 검토. Clerk가 업데이트되면서 기본값이 변경됩니다. "이전 구성"은 시간이 지나면서 조용히 잘못됩니다. 대시보드 JSON 내보내기를 마지막으로 알려진 좋은 사본과 비교하세요.
다음 단계
프로덕션 URL에 대해 FixVibe 스캔을 실행하세요 — baas.clerk-auth0 검사는 Clerk 게시 가능 키, 프로덕션의 테스트 키, 번들된 비밀 키를 플래그합니다. Auth0의 동등한 체크리스트는 Auth0 보안 체크리스트를 참조하세요. BaaS 제공자 전반에 걸친 포괄적 관점은 BaaS 설정 오류 스캐너를 읽으세요.
