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
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -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을 한 방향의 분기·합류 데이터 흐름으로 보여 준다."
}
}