Files
llm-wiki/raw/branch-notes/feature-keycloak-bff-csrf-samesite-defense.md
T

20 KiB

title, source_type, status, id, kind, project, work_item, inherits, refines, overrides, depends_on, imports, delegates, accepts_delegations, contract_packet, contract_packet_sha256, branch, parent_branch, related_projects, tags, created, target_merge, status_label
title source_type status id kind project work_item inherits refines overrides depends_on imports delegates accepts_delegations contract_packet contract_packet_sha256 branch parent_branch related_projects tags created target_merge status_label
branch / feature-keycloak-bff-csrf-samesite-defense branch-note raw BR-KEYCLOAK-PATTERNS-OVERVIEW-011 project-work-item keycloak-patterns-overview WI-KEYCLOAK-PATTERNS-OVERVIEW-011
DEC-KEYCLOAK-PATTERNS-OVERVIEW-ACCEPTANCE-001@1
DEC-KEYCLOAK-PATTERNS-OVERVIEW-BFF-SESSION-001@1
WI-KEYCLOAK-PATTERNS-OVERVIEW-010
1 00ce3ab254b077628852e877be348df58e6c0d84b03466a875ecc7a3c4023e11 feature-keycloak-bff-csrf-samesite-defense
keycloak-patterns-overview
branch
2026-07-23 in-progress

branch: feature-keycloak-bff-csrf-samesite-defense

부모 (필수)

브랜치 계약 패킷

  • 생성 시 프로젝트 개정: 1
  • 패킷 스키마: contract_packet: 1
  • 완료 조건: CSRF를 재현하고 SameSite와 CSRF token 적용 후 403을 검증한다

상속한 프로젝트 결정

Decision Ref Project Summary Branch Application Source
DEC-KEYCLOAK-PATTERNS-OVERVIEW-ACCEPTANCE-001@1 done-bar는 E2E success와 signature security failure 재현·해결 evidence다 WI-KEYCLOAK-PATTERNS-OVERVIEW-011 완료 조건에 적용 [[raw/project-notes/keycloak-patterns-overview]]
DEC-KEYCLOAK-PATTERNS-OVERVIEW-BFF-SESSION-001@1 AP3는 token을 backend session에 두고 browser에는 cookie만 둔다 WI-KEYCLOAK-PATTERNS-OVERVIEW-011 완료 조건에 적용 [[raw/project-notes/keycloak-patterns-overview]]

브랜치 지역 결정

Decision ID Decision Relation Supporting Claims Status

선언한 예외

Override ID Overrides Reason Approval Status

가져온 artifact 계약

Artifact Ref Owner Producer Schema Ref

가져온 프로젝트 계약

Ref Owner 요약 Branch 적용

수신한 위임

Delegation Ref From Concern Status

가져온 흐름 단계

Stage Ref Order Owner Input Action Output

목표

  • WI-KEYCLOAK-PATTERNS-OVERVIEW-011의 완료 조건을 구현한다: CSRF를 재현하고 SameSite와 CSRF token 적용 후 403을 검증한다

범위

포함 범위

  • AP3(BFF) cookie-session 이 CSRF 에 노출됨을 재현(state-changing 요청을 토큰 없이 위조).
  • Spring Security CSRF token(synchronizer token pattern) 적용 후 토큰 없는 위조 요청이 403 으로 차단됨을 검증.
  • SameSite 쿠키 속성(SESSION 쿠키에 Lax)을 defense-in-depth 로 결합(완료조건이 요구) — CSRF token(D2)의 대체가 아닌 보완. 근거: MDN·RFC 6265bis·OWASP·Spring Boot(D3, 2026-07-25 아카이빙 완료).

제외 범위

의도적으로 제외. 면접 등에서 "이건 범위에 없었습니다" 근거.

  • BFF oauth2Login 세션 수립 자체 → WI-010(feature-keycloak-bff-oauth2login-session) 소유. 본 branch 는 그 세션의 CSRF 방어만.
  • token 을 browser 에 노출하는 패턴(AP1/AP2) — 본 branch 는 AP3(browser 는 session cookie 만) 전제.
  • project decision registry 변경.

근거 (필수, 최소 1개+)

Source 정당화하는 결정
[[raw/official-docs/csrf-protection-spring-official]] D1·D2 — Spring Security 는 unsafe method 에 기본 CSRF 방어(SPRINGSEC-CSRF-C1), 토큰 없는 위조 요청 가능(C2), synchronizer token pattern(C3), CookieCsrfTokenRepository=XSRF-TOKEN/X-XSRF-TOKEN(C4), BREACH 방어(C5)
[[raw/project-notes/keycloak-patterns-overview]] 상속 — DEC-…-BFF-SESSION-001@1(AP3=session·browser cookie 만), DEC-…-ACCEPTANCE-001@1(재현→해결 evidence)
[[raw/official-docs/rfc6265bis-samesite-attribute-ietf]] D3 — SameSite 세 값(Strict/Lax/None) 표준 정의(RFC6265BIS-SAMESITE-C1/C2/C3), Lax 가 cross-site top-level navigation(safe method)에 쿠키를 허용하는 rationale(C4), 단 Lax 가 top-level POST 콜백에는 부적합함을 명시하는 경계(C5) — AP3 콜백이 GET/safe-method top-level navigation 인지 확인 후 D3 확정 필요(IETF Internet-Draft, 확정 RFC 아님)
[[raw/official-docs/samesite-set-cookie-mdn-official]] · [[raw/official-docs/spring-boot-session-cookie-samesite-property-official]] · [[raw/official-docs/spring-boot-cookie-samesite-enum-javadoc-official]] D3 — MDN SameSite 서술(MDN-SAMESITE-C1~C4) + Spring Boot 설정 프로퍼티 server.servlet.session.cookie.same-site(SPRINGBOOT-SESSION-SAMESITE-C1). 프로퍼티 페이지가 허용값을 미열거하므로 Cookie.SameSite enum Javadoc 으로 리터럴 값(LAX=SPRINGBOOT-COOKIE-SAMESITE-ENUM-C4, STRICT/NONE/OMITTED=C5/C3/C2)을 보강
[[raw/official-docs/csrf-prevention-cheat-sheet-owasp-samesite-official]] D3 — OWASP CSRF cheat-sheet 는 SameSite 를 defense-in-depth 통제로 명시하고 "does not replace a proper CSRF defense"(OWASP-CSRF-SAMESITE-C1)라 규정 — D2(CSRF token, synchronizer pattern)와 D3(SameSite)를 **결합(combine, 대체 아님)**으로 프레이밍하는 근거(C5: synchronizer token pattern 이 1차 권장 방법). 세션 쿠키 적용 권고(C2), Lax 균형/Strict UX 비용 trade-off(C3·C4)도 포함. 앞 행의 "OWASP CSRF cheat-sheet 미아카이빙(deferred)" 항목은 이 아카이빙으로 해소됨

TODO

  • BFF cookie-session 에 대한 CSRF 위조 요청 재현(토큰 없이 state-changing 성공) — 등급: planned
  • Spring Security CSRF token 적용 → 토큰 없는 위조 403 검증 — 등급: planned
  • SameSite=Lax 를 SESSION 쿠키에 적용(server.servlet.session.cookie.same-site=lax) — 근거 소스 확보 완료(MDN·RFC 6265bis·OWASP·Spring Boot). 착수 시 콜백 method(GET vs form_post) 확인 — 등급: planned

진행 중 메모

  • CSRF 토큰 근거(csrf-protection-spring-official) 확보. SameSite 근거도 확보 완료 (2026-07-25 아카이빙: MDN·RFC 6265bis(IETF Internet-Draft)·OWASP CSRF cheat-sheet·Spring Boot server.servlet.session.cookie.same-site) → D3 는 UNSUPPORTED_DECISION 에서 근거 있는 결정(SESSION 쿠키에 SameSite=Lax, CSRF token 과 병행)으로 승격.
  • 남은 open risk(결정은 성립, 착수 시 검증): (1) AP3 Keycloak 콜백이 GET/safe-method top-level navigation 인지 — response_mode=form_post(POST)면 Lax 가 쿠키를 제외해 로그인 흐름 실패(RFC6265BIS-SAMESITE-C5). (2) XSRF-TOKEN 쿠키의 SameSite 설정 API 는 공식 근거 미확보 → UNSUPPORTED_IMPL_DECISION 유지(SameSite 1차 대상은 SESSION 쿠키). (3) Spring Session(Redis 등) 사용 시 위 프로퍼티가 무시될 수 있음(vendor 이슈, 미검증) — 세션 구현체(DEC-…-BFF-SESSION-001) 확인 선행.
  • AP3 코드 미구현(NO_GROUND_TRUTH) → 검증 등급은 여전히 전부 planned.

결정 사항

  • 2026-07-24: D1 BFF cookie-session 의 CSRF 노출을 먼저 재현(토큰 없는 위조 state-changing 요청) / 이유: acceptance 계약(재현→해결 evidence) / 대안: 재현 생략하고 방어만(evidence 약화) / 근거: [[raw/official-docs/csrf-protection-spring-official]](SPRINGSEC-CSRF-C2)
  • 2026-07-24: D2 Spring Security CSRF token(synchronizer token pattern, 기본 활성 + CookieCsrfTokenRepository) 적용 → 403 / 이유: 공식 기본 방어, JS 클라이언트엔 XSRF-TOKEN 쿠키 노출 필요 / 대안: double-submit only / 근거: [[raw/official-docs/csrf-protection-spring-official]](C1·C3·C4·C5)
  • 2026-07-25: D3 (2026-07-24 UNSUPPORTED_DECISION → 근거 확보로 승격) SESSION 쿠키에 SameSite=Lax 적용(server.servlet.session.cookie.same-site=lax)을 CSRF token(D2)과 병행하는 defense-in-depth / 이유: OWASP 는 SameSite 를 "does not replace a proper CSRF defense"(OWASP-CSRF-SAMESITE-C1)인 보완 통제로 규정하고 synchronizer token 을 1차 권장(OWASP-CSRF-SAMESITE-C5); RFC 6265bis 는 Lax 를 top-level navigation(safe method)을 깨지 않으면서 CSRF 를 완화하는 drop-in 으로 정의(RFC6265BIS-SAMESITE-C2·C4), 외부 IdP(Keycloak) 로그인 redirect(cross-site top-level GET navigation)와 호환(MDN-SAMESITE-C2); OWASP 는 외부 링크 세션 유지에 Lax 가 합리적 균형이라 명시(OWASP-CSRF-SAMESITE-C3); 설정은 Spring Boot server.servlet.session.cookie.same-site(SPRINGBOOT-SESSION-SAMESITE-C1) / 검토한 대안: Strict(cross-site 전면 차단이나 Keycloak 콜백에 세션 쿠키 미전송 → 로그인 흐름 파괴 위험 RFC6265BIS-SAMESITE-C5·OWASP-CSRF-SAMESITE-C4; 순수 same-site 내부 로그인일 때만 적합), None(CSRF 방어 기여 0 + Secure 강제 MDN-SAMESITE-C4 — 배제), CSRF token 단독(SameSite 보완 없음, 완료조건의 SameSite 요구 미충족) / 근거: [[raw/official-docs/rfc6265bis-samesite-attribute-ietf]]·[[raw/official-docs/samesite-set-cookie-mdn-official]]·[[raw/official-docs/csrf-prevention-cheat-sheet-owasp-samesite-official]]·[[raw/official-docs/spring-boot-session-cookie-samesite-property-official]]

Decision Evidence Map / 결정-근거 매핑

D1·D2 는 official-vendor-doc(Spring). D3(SameSite)는 2026-07-25 근거 확보 → official-standard(RFC 6265bis, IETF Internet-Draft) + official-reference(MDN·OWASP) + official-vendor-doc(Spring Boot). Curity 등 company-case-study 단독 official 단언 금지.

Decision ID Decision 선택 조건 (언제 이 결정 / 언제 대안) Supporting Claims Evidence Strength Open Risk
D1 BFF cookie-session CSRF 노출을 재현(토큰 없는 위조 state-changing) acceptance(재현→해결) → 이 결정 / 재현 생략 → evidence 약화 raw/official-docs/csrf-protection-spring-official.md#SPRINGSEC-CSRF-C2 official-vendor-doc C2 는 logout 시나리오 한정 결과 진술 — 일반 state-changing 위조 재현 절차는 구현 상세
D2 Spring Security CSRF token(synchronizer + CookieCsrfTokenRepository + BREACH) 적용 → 403 AP3 JS 클라이언트 → 이 결정(쿠키로 토큰 노출) / server-rendered → form hidden field raw/official-docs/csrf-protection-spring-official.md#SPRINGSEC-CSRF-C1, #SPRINGSEC-CSRF-C3, #SPRINGSEC-CSRF-C4, #SPRINGSEC-CSRF-C5 official-vendor-doc XSRF-TOKEN 쿠키의 SameSite 기본값/설정은 이 소스 범위 밖(C4 Open Risk) → D3 와 연결
D3 SESSION 쿠키에 SameSite=Lax 적용(server.servlet.session.cookie.same-site=lax)을 CSRF token(D2)과 병행 — defense-in-depth(대체 아님) 외부 IdP(Keycloak) cross-site top-level GET 콜백 존재 → Lax / 순수 same-site 내부 로그인만 → Strict 재검토 / cross-site 임베드 요구 → None(+Secure), 본 branch 해당 없음 raw/official-docs/rfc6265bis-samesite-attribute-ietf.md#RFC6265BIS-SAMESITE-C2, #RFC6265BIS-SAMESITE-C4, raw/official-docs/samesite-set-cookie-mdn-official.md#MDN-SAMESITE-C2, raw/official-docs/csrf-prevention-cheat-sheet-owasp-samesite-official.md#OWASP-CSRF-SAMESITE-C1, #OWASP-CSRF-SAMESITE-C3, #OWASP-CSRF-SAMESITE-C5, raw/official-docs/spring-boot-session-cookie-samesite-property-official.md#SPRINGBOOT-SESSION-SAMESITE-C1, raw/official-docs/spring-boot-cookie-samesite-enum-javadoc-official.md#SPRINGBOOT-COOKIE-SAMESITE-ENUM-C4 official-standard + official-reference + official-vendor-doc (1) 콜백이 top-level POST(response_mode=form_post)면 Lax 가 쿠키 제외 → 로그인 실패(RFC6265BIS-SAMESITE-C5); GET 콜백 확인 필요. (2) XSRF-TOKEN 쿠키 SameSite 설정 API 근거 없음 → UNSUPPORTED_IMPL_DECISION. (3) Spring Session 사용 시 프로퍼티 무시 가능(미검증) — 세션 구현체 확인 선행. (4) 리터럴 허용값(LAX/STRICT/NONE/OMITTED)은 Cookie.SameSite enum Javadoc(SPRINGBOOT-COOKIE-SAMESITE-ENUM-C2~C5)으로 확보 — 프로퍼티 기본값 자체는 여전히 미열거
D4 완료 판정 = CSRF 재현 AND (SameSite + CSRF token) 적용 후 403 (상속 acceptance) 항상 / N/A raw/project-notes/keycloak-patterns-overview.md (DEC-KEYCLOAK-PATTERNS-OVERVIEW-ACCEPTANCE-001@1, 상속) project-decision(inherited) D3 근거 확보 완료 → 완료조건의 "SameSite" 부분 충족 가능. 남은 것은 코드 구현 + 콜백 method 검증(D3 Open Risk 1)

구현 가이드

Trace: D1(SPRINGSEC-CSRF-C2) + D2(SPRINGSEC-CSRF-C1/C3/C4/C5) + D3(RFC6265BIS-SAMESITE-C2/C4/C5·MDN-SAMESITE-C2·OWASP-CSRF-SAMESITE-C1/C3/C5·SPRINGBOOT-SESSION-SAMESITE-C1·SPRINGBOOT-COOKIE-SAMESITE-ENUM-C4) + D4(inherited). AP3 코드 부재(NO_GROUND_TRUTH)라 사전 명세, 등급 planned.

  • SESSION 쿠키 SameSite 는 이제 근거 있는 결정(Lax, server.servlet.session.cookie.same-site=lax, D3) — UNSUPPORTED 아님.
  • UNSUPPORTED_IMPL_DECISION: (a) XSRF-TOKEN 쿠키의 SameSite 설정CookieCsrfTokenRepository 쿠키에 SameSite 를 지정하는 공식 API 근거 미확보 → 착수 시 결정. trade-off: SameSite 1차 대상은 실제 인증 상태를 쥔 SESSION 쿠키이므로 SESSION 만 Lax 로도 완료조건 충족 vs XSRF-TOKEN 까지 맞추면 일관성↑(근거 소스 필요). (b) CSRF 재현 벡터 — cross-site auto-submit form / fetch(credentials:'include'). 소스는 위조 가능성만 진술.
항목 사전 명세 (planned) Trace
CSRF 재현 cross-site 에서 cookie 자동첨부로 state-changing POST 위조 성공(토큰 없이) D1(SPRINGSEC-CSRF-C2) · UNSUPPORTED_IMPL_DECISION(b)
CSRF token .csrf() 기본 + CookieCsrfTokenRepository.withHttpOnlyFalse()(vanilla-JS SPA 가 XSRF-TOKEN 쿠키를 읽어 X-XSRF-TOKEN 헤더로 실어야 하므로 httpOnly off — 소스 §적용경계 코드 예제) + 기본 BREACH(XorCsrfTokenRequestAttributeHandler) → 토큰 없는 위조 403 D2(SPRINGSEC-CSRF-C1/C3/C4/C5); withHttpOnlyFalse() 는 C4 범위 밖 API detail(소스 코드 예제 근거)
SameSite (SESSION 쿠키) server.servlet.session.cookie.same-site=lax(리터럴 lax = enum LAX) — cross-site subrequest(CSRF 벡터) 차단 + Keycloak top-level GET 콜백 유지 D3(SPRINGBOOT-SESSION-SAMESITE-C1·SPRINGBOOT-COOKIE-SAMESITE-ENUM-C4·RFC6265BIS-SAMESITE-C2·OWASP-CSRF-SAMESITE-C3)
SameSite (XSRF-TOKEN 쿠키) UNSUPPORTED_IMPL_DECISION(a) — 설정 API 근거 미확보, 착수 시 결정 D3 · UNSUPPORTED_IMPL_DECISION(a)

엣지·실패·의존

  • 실패·엣지 경로:
    • 콜백 method 경계 (D3 확정 전 필수 확인): Keycloak 콜백이 cross-site top-level POST(response_mode=form_post)면 Lax 가 쿠키를 제외해 로그인 흐름이 복구 불가능하게 실패(RFC6265BIS-SAMESITE-C5). OAuth2 authorization code flow 기본은 GET 302 redirect(query response mode)라 Lax 호환이나, 실제 설정이 form_post 가 아님을 착수 시 확인.
    • SameSite=Lax 는 top-level navigation GET 을 허용 → GET 기반 state-changing 엔드포인트가 있으면 우회 가능(그래서 CSRF token 병행 — OWASP-CSRF-SAMESITE-C1: SameSite 는 proper CSRF 방어 대체 아님). REST 정합(GET=safe method) 설계면 이론적 갭.
    • CSRF token 이 SPA fetch 에 누락 → 정상 요청도 403. JS 가 XSRF-TOKEN 쿠키를 읽어 X-XSRF-TOKEN 헤더로 전송해야 함(SPRINGSEC-CSRF-C4).
    • Spring Session 상호작용: 세션이 Spring Session(@EnableSpringHttpSession, Redis 등)으로 구현되면 server.servlet.session.cookie.same-site 가 무시될 수 있다는 vendor 이슈 보고(미검증, official-doc 아님 — SPRINGBOOT-SESSION-SAMESITE-C1 boundary). 세션 구현체 확인 후 프로퍼티 실효성 재검증.
  • 다른 계약 의존: WI-KEYCLOAK-PATTERNS-OVERVIEW-010(feature-keycloak-bff-oauth2login-session)의 cookie-session(DEC-…-BFF-SESSION-001@1)을 consume. 세션 쿠키 이름·속성(SameSite 포함)·세션 저장소 구현체(순정 서블릿 vs Spring Session)가 바뀌면 D2·D3 영향 — 특히 D3 의 SameSite 프로퍼티 실효성은 그 구현체에 의존.

검증해야 할 주장 / Claims To Verify

공식 문서는 메커니즘 근거지만 본 프로젝트 실제 동작을 자동 보장하지 않는다. SameSite 소스는 확보됐으나(D3) AP3 코드 미구현이라 구현 후 검증.

Claim Why uncertain How to verify Status
BFF cookie-session 에 CSRF 위조 요청이 토큰 없이 성공(재현) AP3 코드 미구현 · 재현 벡터 미정(b) cross-site auto-submit form 으로 state-changing 요청 → 성공(방어 전) 확인 planned
CSRF token 적용 후 토큰 없는 위조가 403 코드 미구현 .csrf()+CookieCsrfTokenRepository 적용 → 토큰 없는 위조 403, 정상(헤더 포함) 200 확인 planned
SESSION 쿠키 SameSite=Lax 가 cross-site 위조 subrequest 를 차단하고 Keycloak 콜백은 유지 코드 미구현 · Lax 는 top-level GET 허용이라 콜백 method 에 결과 의존 server.servlet.session.cookie.same-site=lax 적용 → (a) cross-site auto-submit POST 위조가 쿠키 미첨부로 차단 (b) Keycloak 로그인 redirect 정상 완료 확인 planned
Keycloak 콜백이 GET/safe-method top-level navigation (POST/form_post 아님) AP3 코드·Keycloak client 설정 미구현(NO_GROUND_TRUTH) — POST 면 Lax 로 로그인 실패(RFC6265BIS-SAMESITE-C5) Keycloak client response_mode·리다이렉트 로그 확인 → GET 302 확인 needs-confirmation
server.servlet.session.cookie.same-site 가 실제 세션 쿠키에 적용됨(Spring Session 미간섭) 세션 구현체 미확정 — Spring Session 사용 시 무시 가능성(vendor 이슈, 미검증) 응답 Set-Cookie 헤더에서 SESSION 쿠키의 SameSite=Lax 실측 needs-confirmation

관심사 커버리지 (coverage-auditor 자동 생성 — 있을 때)

/coverage 실행 전.

마주친 문제

아직 없음.

묶음 (이 branch에서 파생된 자료)

관련 일일 노트

해당 없음.

완료 후 정리

  • PR 링크:
  • 리뷰 메모: