chore: readme 수정

This commit is contained in:
DongHyeonka
2026-07-29 18:03:21 +09:00
parent 8daa568746
commit 741c79b69f
180 changed files with 62890 additions and 13 deletions
@@ -0,0 +1,222 @@
{
"version": "1.1",
"id": "ap3-csrf-boundary",
"title": "AP3의 masked CSRF 응답과 raw POST credential",
"question": "AP3에서 CSRF 응답의 masked token과 raw cookie 중 무엇이 실제 POST header의 data source가 되는가?",
"type": "data-flow",
"direction": "LR",
"audience": [
"Spring BFF의 cookie 기반 CSRF 경계를 구현·검토하는 개발자"
],
"summary": "BFF는 raw XSRF cookie와 masked JSON token을 함께 내보내지만, SPA는 raw cookie를 읽어 같은 raw 값을 POST header에 넣고 Spring CSRF filter가 cookie와 header를 비교한다.",
"alt": "BFF CSRF endpoint가 raw XSRF cookie와 masked JSON token으로 분기하고, SPA가 raw cookie만 실제 POST header 값으로 사용해 Spring CSRF filter에 제출하는 데이터 흐름.",
"long_description": "왼쪽의 BFF CSRF endpoint에서 두 결과가 갈라진다. XSRF-TOKEN cookie에는 raw token이 저장되고 JSON body에는 XOR와 Base64로 masked된 token 및 headerName이 담긴다. 두 결과는 SPA의 POST 조립 단계로 모이지만, JSON에서는 headerName만 사용하고 실제 X-XSRF-TOKEN 값은 document.cookie에서 읽은 raw token이다. POST에는 같은 raw 값을 가진 cookie와 header가 함께 도달하고 Spring CSRF filter가 일치 여부를 확인한다.",
"source_context": {
"document": "document.md",
"document_sha256": "df4d1a604c74e756672b5b40510abfedb8c67b39af280a5f51985ea9972f5371",
"anchor": {
"kind": "marker",
"value": "ap3-csrf-boundary",
"line": 858
}
},
"composition": {
"profile": "component-flow",
"diagram_only": true,
"reference_ids": [
"payment-event-flow"
],
"rationale": "CSRF endpoint의 한 응답이 raw cookie와 masked JSON으로 분기한 뒤 SPA request assembly에서 다시 합쳐져 filter 검증으로 이어지는 명시적 데이터 경로가 중심이므로 component-flow가 적합하다.",
"focus_node": "raw-cookie"
},
"groups": [],
"nodes": [
{
"id": "csrf-endpoint",
"label": "BFF · /bff/csrf",
"kind": "service",
"role": "source",
"description": "CookieCsrfTokenRepository와 CsrfController를 통해 raw cookie와 masked JSON 응답을 만드는 endpoint.",
"evidence": [
{
"start_line": 819,
"end_line": 844
}
],
"assumption": false
},
{
"id": "raw-cookie",
"label": "Browser cookie · raw",
"kind": "credential",
"role": "store",
"shape": "box",
"details": [
"XSRF-TOKEN",
"JavaScript-readable",
"실제 header data source"
],
"emphasis": "primary",
"description": "Cookie repository가 path /에 설정하며 SPA가 document.cookie로 읽는 raw CSRF token.",
"evidence": [
{
"start_line": 827,
"end_line": 834
},
{
"start_line": 844,
"end_line": 856
}
],
"assumption": false
},
{
"id": "masked-json",
"label": "JSON body · masked",
"kind": "data",
"role": "store",
"shape": "document",
"details": [
"token = XOR/Base64",
"headerName metadata",
"POST token 값으로 미사용"
],
"description": "Request attribute용 token을 masked한 JSON 표현으로, SPA는 token 값이 아니라 headerName만 사용한다.",
"evidence": [
{
"start_line": 836,
"end_line": 846
}
],
"assumption": false
},
{
"id": "post-assembler",
"label": "SPA POST 조립",
"kind": "component",
"role": "service",
"details": [
"Cookie 자동 첨부",
"document.cookie raw → header",
"JSON headerName만 사용"
],
"description": "Raw cookie 값을 X-XSRF-TOKEN header에 넣어 preference POST를 만드는 browser code.",
"evidence": [
{
"start_line": 846,
"end_line": 856
},
{
"start_line": 862,
"end_line": 870
}
],
"assumption": false
},
{
"id": "csrf-filter",
"label": "Spring CSRF filter",
"kind": "security",
"role": "sink",
"details": [
"raw cookie = raw header 비교",
"일치 → controller",
"부재·불일치 → 403"
],
"description": "Repository의 expected raw token과 submitted raw header를 controller 실행 전에 비교하는 방어선.",
"evidence": [
{
"start_line": 854,
"end_line": 854
},
{
"start_line": 873,
"end_line": 873
},
{
"start_line": 897,
"end_line": 904
}
],
"assumption": false
}
],
"edges": [
{
"id": "endpoint-to-raw-cookie",
"from": "csrf-endpoint",
"to": "raw-cookie",
"label": "Set-Cookie · raw",
"kind": "response",
"evidence": [
{
"start_line": 827,
"end_line": 834
}
],
"assumption": false
},
{
"id": "endpoint-to-masked-json",
"from": "csrf-endpoint",
"to": "masked-json",
"label": "masked JSON",
"kind": "response",
"evidence": [
{
"start_line": 836,
"end_line": 844
}
],
"assumption": false
},
{
"id": "raw-cookie-to-assembler",
"from": "raw-cookie",
"to": "post-assembler",
"label": "document.cookie · raw",
"kind": "data",
"emphasis": "primary",
"evidence": [
{
"start_line": 846,
"end_line": 856
}
],
"assumption": false
},
{
"id": "masked-json-to-assembler",
"from": "masked-json",
"to": "post-assembler",
"label": "headerName only",
"kind": "data",
"evidence": [
{
"start_line": 846,
"end_line": 856
}
],
"assumption": false
},
{
"id": "assembler-to-filter",
"from": "post-assembler",
"to": "csrf-filter",
"label": "Cookie raw = Header raw",
"kind": "request",
"emphasis": "primary",
"evidence": [
{
"start_line": 848,
"end_line": 873
}
],
"assumption": false
}
],
"legend": [],
"metadata": {
"rationale": "Token masking 자체보다 실제 state-changing request의 credential source와 server comparison을 한 방향의 분기·합류 데이터 흐름으로 보여 준다."
}
}