Skip to content

[BE][Security] cookie 인증 API CSRF 방어 도입 #145

Description

@HyungminYoon1

문제

Gateway는 HttpOnly auth_token cookie를 인증 수단으로 사용하지만 cookie-authenticated state mutation에 중앙 CSRF 방어가 없다. CORS와 SameSite 설정만으로는 CSRF 보안 경계를 대체할 수 없다. 특히 non-local cookie가 SameSite=None이므로 unsafe mutation은 별도 request token과 exact Origin 검증 없이 활성화할 수 없다.

목표

Cookie authentication을 실제로 수용하는 모든 unsafe mutation에 일관된 signed double-submit CSRF, exact Origin과 Fetch Metadata 검증을 적용하고 public/Bearer/API-secret surface와 명확히 분리한다.

확정 방식

  • GET /api/v1/auth/csrf가 짧은 TTL의 signed token을 응답 본문으로 반환하고 같은 값을 host-only HttpOnly CSRF cookie로 설정한다.
  • Client는 token을 메모리에만 보관하고 unsafe request의 X-CSRF-Token으로 보낸다. URL, localStorage/sessionStorage, log, trace와 audit에는 저장하지 않는다.
  • 서버는 header-cookie constant-time equality와 HMAC 서명을 검증한다. 서명 입력은 version, expiry, random nonce, 현재 auth session fingerprint와 active organization scope를 포함하며 auth cookie 원문은 token에 포함하지 않는다.
  • 로그인 전 signup/login은 별도 HttpOnly anonymous seed에 결박된 pre-auth token을 사용한다. 로그인 성공, organization 전환, logout과 만료 뒤에는 새 token을 발급받아야 한다.
  • 같은 session·organization·TTL 안의 token 재사용은 병렬 요청을 위해 허용한다. 다른 session/organization, logout 이후 또는 expiry 뒤 replay는 거부한다.
  • Google OAuth navigation/callback은 custom header를 사용할 수 없으므로 기존 signed one-time OAuth state/session 검증을 login-CSRF 경계로 유지하고 일반 cookie mutation 예외로 명시한다.

적용 범위

  • Cookie를 수용하는 POST, PUT, PATCH, DELETE
  • Signup, password login과 logout의 session 변경 surface
  • App, Workflow, permission, Organization/Team, Knowledge, credential, deployment, budget mutation
  • authenticated internal Chatbot과 향후 Conversation Memory mutation
  • 공통 Client API adapter의 token bootstrap, rotation 및 CSRF 실패 1회 refresh/retry

중앙 집행과 route inventory

  • Gateway의 중앙 middleware/policy가 method, route audience/auth scheme과 cookie 존재를 기준으로 unsafe 요청을 판정한다.
  • Cookie-authenticated mutation은 개별 controller가 검사를 빠뜨려도 중앙에서 fail-closed한다.
  • Public, Bearer-only, API-secret, webhook surface는 명시적인 route inventory와 정책 enum으로만 면제한다. Public route에 login cookie가 함께 전송되어도 authenticated surface로 승격하거나 불필요한 CSRF 권한을 부여하지 않는다.
  • Architecture test는 등록된 모든 unsafe route가 cookie/pre-auth/public/Bearer/API-secret 중 정확히 하나로 분류되는지 검사하고 미분류·중복 분류를 실패시킨다.

검증 계약

  • browser unsafe request는 configured CORS_ORIGINS의 exact Origin을 필수로 하며 missing, null, malformed와 unlisted 값을 거부한다.
  • Sec-Fetch-Site가 제공되면 same-origin 또는 exact-Origin과 정합한 same-site만 허용하고 cross-site를 거부한다.
  • CORS preflight 성공을 CSRF 성공으로 간주하지 않는다.
  • JSON mutation은 application/json만 허용한다. 승인된 multipart upload는 exact route inventory, CSRF header와 Origin 검증을 모두 요구하며 simple form 우회로 사용하지 못하게 한다.
  • 검증은 request body parsing, DB mutation, queue publish, provider call과 Memory write 전에 수행한다.
  • 실패는 동일한 sanitized 403 auth.csrf_validation_failed를 반환한다. Audit/metric에는 token·Origin 원문 없이 bounded reason enum만 기록한다.

제외/분리

  • Public webhook/run/chatbot은 cookie를 인증 주체로 사용하지 않는 별도 route다.
  • Public route에 login cookie가 함께 전송되어도 optional authentication으로 승격하지 않는다.
  • API secret/Bearer-only server-to-server route는 별도 replay/rate-limit 계약을 사용한다.
  • Embed Origin/CSP parent policy는 [Security][Chatbot] Embed Origin·CORS·CSP 보안 경계 분리 #353/ADR-0043이 소유하며 CSRF allowlist로 재사용하지 않는다.
  • JWT server-side revocation, MFA와 session store 전환은 이 이슈 범위가 아니다.

TDD

  • missing/mismatched/forged/expired token
  • 다른 session·organization과 logout 뒤 token replay
  • missing/null/unlisted/malformed Origin과 cross-site Fetch Metadata
  • allowed same-origin/same-site exact-Origin mutation
  • JSON, simple form과 승인 multipart 예외
  • login/signup pre-auth token, OAuth state 예외, logout rotation
  • public route가 cookie를 무시하고 내부 권한으로 승격하지 않는지 검증
  • route inventory의 미분류·중복·새 unsafe route 회귀
  • RAG answer, deployment, permission, internal Chatbot/Memory mutation의 provider/DB/queue side effect 이전 차단
  • Client 동시 요청, token bootstrap, organization 전환과 실패 1회 refresh/retry

완료 조건

  • Cookie-authenticated mutation은 CSRF token, exact Origin과 Fetch Metadata 검증 없이 실행되지 않는다.
  • 실패는 일관된 sanitized 403을 반환하고 외부 side effect와 민감 audit payload가 없다.
  • Client API adapter가 token lifecycle을 처리하고 token을 URL/persistent storage/log에 남기지 않는다.
  • Public/Bearer/API-secret route는 명시적 분류로만 면제되고 login cookie로 audience가 바뀌지 않는다.
  • Auth/API/Client 공식 문서, Accepted ADR, deployment 설정과 route inventory가 구현과 일치한다.

관련·소비 이슈

Metadata

Metadata

Assignees

Type

No type

Fields

Priority

None yet

Projects

Status
Done

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions