[Security] OAuth2.0 OpenID Connect nonce
서비스에서 OAuth2.0 OIDC 프로토콜로 SSO 처리를 해두었는데, ID Token을 받을 때 nonce를 설정하는 것을 권장한다고 한다.
그런데 설정 안 해도 동작하고, 해놔도 별다른 조치를 안 했음에도 동작한다.
나중에 안 사실이지만 nonce를 전혀 활용도 하고 있지 못하는 주제에 "ID Token에 nonce 필드 넣어놨으니 안전함~"이러고 있었던 것.
그래서 이 nonce란 어디서부터 시작한 거고, 어떻게 서버에서 다뤄줘야 하는 건지 정리해두려 한다.
조만간 암호학 공부했던 내용도 포스팅 해야겠다..
참고로 OIDC 정책이 뭔지는 이전 포스트에서 정리해두었으므로, 여기서 다시 다루진 않는다.
📌 시스템 간의 통신에서의 보안
OAuth가 아니더라도 인터넷 환경에선 아래 보안 요구사항을 충족하는지 점검해볼 필요가 있다.
1️⃣ 기밀 유지 (암호화)
- 보안의 가장 기본적인 요소.
- 공격자가 패킷을 탈취(Sniffing)하더라도 데이터를 읽을 수 없게 만들어야 한다.
- Sha-256으로 암호화하면 대부분 해결된다. 현재 인간이 보유한 기술력으로 Sha-256이 만든 모든 경우의 수를 계산하는 것은 무리다.
Sha 알고리즘 내용이 진짜 재밌는데, 최대한 빨리 포스팅 해보는 걸로
2️⃣ 신원 확인 (자격 증명)
- 인증(Authenticated)된 클라이언트만이 통신을 허용하도록 한다.
- 클라이언트는 자격 증명(ID/PW, token, cookie 등)을 제출해야 한다.
3️⃣ 변조 방지 (디지털 서명)
- 공격자가 패킷을 가로채서 변경하지 못하게 막는다.
- 서버는 요청이 중간에 변조되지 않았음을 확인하기 위해, 비밀키/공개키 쌍을 사용한다. (ex. https 통신)
깊이 들어가면 끝이 없는 내용이므로, 요것도 다음에.
4️⃣ 재생 방지
- 공격자가 중간에 패킷을 가로챈 후, 그대로 다시 서버로 전송하는 것을 막는다.
이게 왜 보안 위협이지 싶겠지만, OAuth에선 변조 방지와 재생 방지가 훨씬 중요하다.
🤔 OAuth에서는 왜 변조 방지와 재생 방지에 중점을 둘까?
OAuth 인증에 참여하는 Actor는 Client, Provider Server, Authentication Server가 있다.
여기서 Client와 Provicer Server 간의 통신은 우리가 고려하지 않아도 된다. 여기서 발생한 보안 구멍은 Provider 측의 책임이다.
Client와 Provider의 통신이 끝나면, Authentication Server와 통신을 할 텐데 이 과정에서는 비밀번호 같은 기밀을 유지해야 하는 민감한 데이터가 없으므로 과도한 암호화는 불필요하다.
그저 인증된 클라이언트에 의한 유효한 요청인지를 판단하는 것이 더 중요하다.
변조 방지를 위해서는 HTTPS를 적용하면 된다.
재생 방지를 위해선 nonce 값을 적용해야 한다.
📌 OAuth2.0 state vs. OpenID Connect nonce
OAuth2.0의 state와 OIDC의 nonce는 겉보기에 상당히 유사해보이지만, 둘은 전혀 다른 목적을 갖는다.
🟡 state
- OAuth 2.0 위협 모델 및 보안 고려 사항에 소개된 방법으로 CSRF 공격을 방어하기 위한 목적을 갖는다.
- Provider에게 인증 요청 후, 응답 메시지에 대한 유효성을 검증한다.
- Client는 임의의 난수값을 state에 담아 Provider에 사용자 정보를 송신하고, Provider는 redirect 값에 state 포함시킨다. Client는 로컬에 보관한 state값과 불일치할 경우, CSRF 공격이라 간주하고 응답 패킷을 거부한다.
- state는 Client와 Provider 간의 보안을 위한 정책이다.
🟡 nonce
- OpenID Connect 스펙에서 도입되었으며, 재생 공격(Replay Attack)을 방어하기 위한 목적을 갖는다.
- 마찬가지로 client가 난수값을 생성해 nonce를 생성하고, Provider에게 전달해 ID Token에 삽입한다.
- nonce는 모든 요청에서 고유함이 보장되어야 한다.
- id token은 Client와 Authenticate Server 간의 보안을 위한 정책이다.
📌 nonce는 어떻게 재생 공격을 막는가?
ID Token을 활용한 로그인 방식은 일반적으로 위 플로우를 따른다.
Provider가 ID Token을 발급받으면, 기본적인 사용자 정보를 갖는 ID Token을 인증 서버로 전달한다.
인증 서버에서는 ID Token이 유효한 Provider에게 받아왔다는 것을 검증하여, 사용자의 정보를 승인한다.
이러한 과정 덕분에 Packet이 도중에 sniffing 당하더라도, ID Token 정보를 조작했을 때 변조 및 위조 요청에 대한 방어를 수행할 수 있다. (https를 적용했다면 더더욱 변조 공격은 성공할 수 없다.)
하지만 다음과 같은 케이스에서 문제가 발생한다.
Packet을 탈취한 후, 공격자가 아무런 데이터를 수정하지 않고 인증 서버로 전달하면 어떻게 될까?
인증 서버 입장에선 서명의 검증이 통과되므로 공격자에게도 로그인 성공 처리 응답을 돌려주게 된다.
이러한 재생 공격을 막기 위해서 nonce를 사용한다.
인증서버는 ID Token을 검증하기 전에 nonce를 확인해야 하는데, 만약 한 번도 받은 적 없는 값이라면 요청을 승인하고 nonce 값을 캐싱한다.
이후 공격자가 재생 공격을 시도하더라도, 이미 이전에 요청에 사용된 nonce이므로 요청을 거부한다.
📌 Timestamp
이 방법은 재생 공격을 확실히 막을 수 있지만, 인증 서버가 모든 요청의 nonce 값을 보관해야 한다는 부담을 갖는다.
부담을 줄이기 위해선 OAuth 인증 요청을 생성한 시점의 Timestamp 값을 사용하면 된다.
Nonce의 유일성 체크보다도 앞서 Timestamp를 확인하여, 애초에 유효 시간 외의 요청이라면 거부할 수 있다.