20 KiB
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 |
|
|
1 | 00ce3ab254b077628852e877be348df58e6c0d84b03466a875ecc7a3c4023e11 | feature-keycloak-bff-csrf-samesite-defense |
|
|
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 vsform_post) 확인 — 등급:planned
진행 중 메모
- CSRF 토큰 근거(
csrf-protection-spring-official) 확보. SameSite 근거도 확보 완료 (2026-07-25 아카이빙: MDN·RFC 6265bis(IETF Internet-Draft)·OWASP CSRF cheat-sheet·Spring Bootserver.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 Bootserver.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-C1boundary). 세션 구현체 확인 후 프로퍼티 실효성 재검증.
- 콜백 method 경계 (D3 확정 전 필수 확인): Keycloak 콜백이 cross-site top-level POST(
- 다른 계약 의존:
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에서 파생된 자료)
- raw/official-docs/csrf-protection-spring-official
- raw/official-docs/spring-boot-session-cookie-samesite-property-official
- raw/official-docs/samesite-set-cookie-mdn-official
- raw/official-docs/rfc6265bis-samesite-attribute-ietf
- raw/official-docs/csrf-prevention-cheat-sheet-owasp-samesite-official
- raw/official-docs/spring-boot-cookie-samesite-enum-javadoc-official
관련 일일 노트
해당 없음.
완료 후 정리
- PR 링크:
- 리뷰 메모: