refactor: 문서 개선 중
This commit is contained in:
@@ -0,0 +1,97 @@
|
||||
---
|
||||
id: 004dd0a2-5fb3-4f25-80c9-576f709de331
|
||||
kind: REFERENCE
|
||||
slug: forward-auth-identity-header-trust
|
||||
title: Forward-Auth에서 Identity Header를 신뢰하기 위한 조건
|
||||
topic: OAuth/OIDC 인증 경계
|
||||
project: KeyCloak Patterns
|
||||
status: 게시 전
|
||||
version: 10
|
||||
studio: "https://hyeonworks.com/studio/documents/004dd0a2-5fb3-4f25-80c9-576f709de331/edit"
|
||||
---
|
||||
|
||||
# Forward-Auth에서 Identity Header를 신뢰하기 위한 조건
|
||||
|
||||
upstream이 사용자를 판단하는 근거가 헤더 하나뿐인 구조에서, 그 헤더를 믿을 수 있게 만드는 조건을 모았다. 외부 경로 차단, 동명 헤더 덮어쓰기, internal credential 검증이 서로 다른 곳에 함께 있어야 한다.
|
||||
|
||||
## 관계
|
||||
|
||||
- **Forward-Auth에서 Client가 보낸 Identity Header를 신뢰하면 안 되는 이유**
|
||||
이 기준의 다섯 조건을 실제 설정에서 확인한 기록이다.
|
||||
- **Forward-Auth 구조에서 Application Authorization을 어디까지 Edge에 둘 것인가**
|
||||
헤더를 어디까지 늘릴지가 이 기준의 미결 항목이다.
|
||||
- **OAuth Token과 Application Session을 구분하는 기준**
|
||||
identity 헤더를 JWT나 session과 같은 이름으로 부르지 않는다.
|
||||
|
||||
## 목적
|
||||
|
||||
외부 요청이 edge를 지나 인증되고 upstream으로 가는 구조에서, upstream이 사용자를 판단하는 근거는 헤더 하나다.
|
||||
|
||||
같은 이름의 헤더를 인증을 마친 edge가 만들 수도 있고 공격자가 요청에 직접 적어 보낼 수도 있다. upstream이 받는 요청에서 이 둘은 구분되지 않는다.
|
||||
|
||||
identity header를 upstream에서 사용하려면 먼저 그 헤더가 edge를 통해 생성됐음을 보장하는 경로와 검증 방법을 정한다.
|
||||
|
||||
## 규칙
|
||||
|
||||
### 1. 외부에서 upstream과 auth proxy에 직접 닿지 못하게 한다
|
||||
|
||||
edge만 공개하고 나머지는 내부 network에 두면서 host port로 노출하지 않는다.
|
||||
|
||||
이걸 안 하면 공격자가 edge를 건너뛰고 upstream을 직접 부른다. 그때는 헤더를 아무리 검사해도 공격자가 그 헤더를 마음대로 쓸 수 있어서 의미가 없다.
|
||||
|
||||
### 2. client가 보낸 동명 헤더를 항상 덮어쓴다
|
||||
|
||||
merge가 아니라 덮어쓰기로 채우고, 인증 결과에서 복사한 값만 upstream으로 보낸다. merge로 두면 client가 보낸 값이 앞이나 뒤에 함께 붙고, 어느 쪽을 읽을지는 upstream 구현에 달려 있다.
|
||||
|
||||
trusted proxy 범위도 같이 좁힌다. 넓게 잡으면 같은 network 안의 다른 workload가 edge인 척할 수 있고, forwarded 계열 헤더를 믿는 설정에서는 그 범위가 곧 신뢰 경계다.
|
||||
|
||||
### 3. auth endpoint는 subrequest 전용으로 둔다
|
||||
|
||||
이 endpoint는 외부 client가 쓰라고 만든 것이 아니다. proxy가 만드는 subrequest만 들어가게 하고 외부 호출에는 응답하지 않게 둔다. Nginx라면 `internal` location이 그 역할을 한다.
|
||||
|
||||
### 4. upstream이 헤더 존재만 보지 않는다
|
||||
|
||||
배포 시 주입한 internal credential과 요청 값을 비교한다. 비교 구현은 입력값의 일치 길이에 따라 실행 시간이 크게 달라지지 않는 방식을 사용한다.
|
||||
|
||||
internal credential 검증을 controller마다 반복하면 새 endpoint에서 누락될 수 있다. 운영에서는 filter, interceptor, security chain 등 공통 처리 경로에 적용한다.
|
||||
|
||||
### 5. Network 격리와 헤더 검증을 모두 적용한다
|
||||
|
||||
격리는 밖에서 들어오는 직접 접근을 막고 헤더 검증은 안에서 만들어진 위조를 막는다. 막는 대상이 달라서 하나로 다른 하나를 대체했다고 쓸 수 없다.
|
||||
|
||||
### 6. 전달할 헤더를 allowlist로 고정한다
|
||||
|
||||
복사할 응답 헤더 목록을 정해 두고 그 밖은 버린다. 늘릴 때마다 claim 출처와 다중 값 구분자, escaping, 최대 크기, upstream 검증 계약을 다시 정해야 한다.
|
||||
|
||||
user와 email만 전달하는 구조는 누가 왔는지만 말하고 무엇을 해도 되는지는 말하지 않는다. role이 바뀌었을 때 proxy session과 downstream 인가가 언제 따라가는지도 따로 정한다.
|
||||
|
||||
### 7. 검사 지점은 요청 실패가 아니라 응답의 사용자다
|
||||
|
||||
위조 헤더를 얹은 정상 session 요청은 정상 session이니 200이 되는 것이 맞다. 확인할 값은 그 응답의 사용자가 위조 값인지 실제 인증된 사용자인지다. 요청이 실패하는지만 보면 덮어쓰기가 동작하는지 알 수 없다.
|
||||
|
||||
### 8. 지금 확인한 것과 운영에서 더 필요한 것을 나눠 적는다
|
||||
|
||||
이 기준에서 실제 fixture로 확인한 것은 외부 경로 차단, 헤더 덮어쓰기, auth endpoint 내부 전용 지정, upstream의 internal credential 확인이다.
|
||||
|
||||
운영에서는 여기에 더 필요하다. 공유 secret을 secret manager에서 주입하고 교체 절차를 두는 것, network policy로 경로를 강제하는 것, 그리고 더 강하게 묶으려면 mTLS나 workload identity를 쓰는 것이다. 두 묶음을 같은 문단에 섞어 적지 않는다.
|
||||
|
||||
## 적용 조건
|
||||
|
||||
- upstream에 OAuth client나 JWT 검증 코드를 넣기 어려울 때
|
||||
- 여러 legacy service 앞에 같은 로그인 정책을 둘 때
|
||||
- edge에서 정책을 강제할 수 있을 때
|
||||
- 이미 forward-auth를 쓰고 있는 구조를 점검할 때
|
||||
|
||||
## 예외
|
||||
|
||||
- backend 직접 경로나 헤더 덮어쓰기를 닫을 수 없는 환경이면 이 구조를 쓰지 않는다.
|
||||
- 애플리케이션이 사용자별 API 조합과 세밀한 인가를 직접 맡아야 하면 BFF 구조가 더 자연스럽다.
|
||||
- 임의 경로와 body, streaming을 그대로 넘기는 범용 reverse proxy가 필요하면 URI rewrite와 timeout, 응답 헤더 처리를 따로 설계해야 한다.
|
||||
|
||||
## 예시
|
||||
|
||||
- 외부에는 edge만 공개하고 app과 auth proxy의 port는 host에 publish하지 않는다
|
||||
- 정상 session에 위조 헤더를 얹은 요청은 200을 받지만 응답의 사용자는 실제 사용자다
|
||||
- 외부에서 auth endpoint를 직접 부르면 404가 된다
|
||||
- upstream은 user 헤더와 internal token을 함께 확인하고 하나라도 어긋나면 401을 돌려준다
|
||||
- 내부 검사가 controller 하나에만 있으면 새 endpoint에는 보호가 따라오지 않는다
|
||||
Reference in New Issue
Block a user