OAuth2의 SSO 이해하기

letsbebrave·2023년 4월 1일
0

Security

목록 보기
1/1

OAuth2의 핵심은 타사 웹사이트나 웹이 리소스에 접근할 수 있게 허용하는 것이 주목적이다.

공부하면서 몇 가지 의문점이 계속 들었는데, 이 부분을 좀 깊이 있게 알아봐야 할 것 같다.

SSO를 이용하면 애플리키이션 별로 사용자 민감 정보를 관리해줄 필요가 없다?

여기서 개념을 정확히 생각하면서 이해해야 한다.
하단의 애플리케이션은 "클라이언트"를 의미한다.
클라이언트는 사용자를 대신해 사용자가 소유한 리소스에 접근하는 애플리케이션을 의미한다.

SSO는 여러 애플리케이션("클라이언트")에 대한 인증을 중앙 집중적으로 관리하기 때문에, 각 애플리케이션마다 별도의 인증 정보나 사용자 데이터를 저장하고 관리할 필요가 없다. 사용자가 한 번 로그인하면, 인증 서버에서 발급한 인증 토큰을 사용하여 다른 애플리케이션에도 쉽게 로그인할 수 있다.

이 말을 좀 더 풀어 쓰면, OAuth2의 특징인 Access Token을 이용해서 다양한 애플리케이션에 접속이 가능한데, 이 때 자격 증명을 관리하는 방식이 각 애플리케이션마다 관리하는 것이 아니라, 인증 서버라는 곳에서 사용자의 자격 증명을 관리하고 조직이 소유한 모든 애플리케이션이 인증 서버를 통해 사용자를 인증할 수 잇는 것을 말한다.

따라서 저 질문을 조금 더 다듬으면, SSO를 이용하면 애플리케이션 별로 사용자 자격 증명을 관리해줄 필요가 없다는 것을 의미한다.
이것의 장점은, 사용자 자격 증명을 관리하는 곳은 인증 서버라는 구성 요소로 격리하여 유지 관리를 편하게 할 수 있다. 또한 인증 서버에서는 Access Token을 제시하여 네트워크 상에 자격 증명을 노출하지 않고도 같은 자격 증명으로 다양한 서비스에 접속할 수 있다.

승인 코드 그랜트 유형에서 한 번에 엑세스 토큰을 발급하지 않는 이유는?

승인 코드 그랜트 유형에서는 한 번에 엑세스 토큰을 발급하지 않고, 먼저 인증 코드를 발급한다.

위의 사진에서 Identity Provider는 인증 서버, Service Provider는 리소스 서버라고 보면 된다.

그런데, 5번째 과정에서 인증 서버에서 해당되는 고객의 인증을 처리한 이후, 바로 Access Token을 발급하는 것이 아니라 Authorization Code를 발급한다.

근데 왜 액세스 토큰을 한번에 발급해주지 않는 걸까?

인증 코드를 사용하면 더 안전한 이유는 인증코드와 클라이언트 아이디와 클라이언트 시크릿을 함께 보내는데, 이때 공격자는 클라이언트 시크릿을 알아내기 힘들기 때문에 더 안전해진다.
또한 인증 서버 입장에서도 어느 클라이언트가 접속한지 확인하고 액세스 토큰을 줄 수 있다.

암시적 그랜트 유형에서는 인증 서버가 실제 올바른 클라이언트에서 받았는지 확인하지 않은 엑세스 토큰을 리디렉션 URI로 곧바로 호출했기 때문에 덜 안전하다.

그러나 인증 코드와 클라이언트 시크릿을 함께 보내서 클라이언트가 액세스 토큰을 얻기 위해 자격 증명을 해야 하므로 자신이 누구인지 다시 증명하게 함으로써 더 안전하다.

정리하면 암시적 그랜트 유형은 바로 액세스 토큰을 발급해주는데, 이 방법은 클라이언트 시크릿을 사용하지 않기 때문에 인증 서버는 실제로 올바른 클라이언트에서 요청을 받았는지 확인할 수 없다. 또한 리디렉션 URI로 엑세스 토큰을 직접 전달하므로 보안성이 낮다.
그러나 인증 코드 그랜트 유형에선 액세스 토큰을 받기 전에 클라 시클릿과 인증 코드를 받아서 다시 한 번 클라를 검증하고 액세스 토큰을 준다. 또한 리디렉션 URI로 엑세스 토큰을 전달하지 않고 응답을 해주는 방식이어서 더 안전하다.

리디렉션 URI vs. Response

리디렉션 URI

일반적으로 쿼리 파라미터 사용하여 전달된다.
인증 코드를 반환할 때 인증 코드 그랜트 유형도 클라이언트 서버로 인증 코드를 반환해주지만, 공격자는 인증 코드가 있어도 클라이언트 서버의 시크릿을 모르기 때문에 괜찮다.
암시적 그랜트 유형에서는 토큰을 리디렉션 URI에 담아서 전달하기 때문에 해당 토큰이 노출된다.

https://client.example.com/callback?access_token=SplxlOBeZQQYbYS6WxSbIA&token_type=Bearer&expires_in=3600

Response

인증 코드 그랜트 유형의 경우, 액세스 토큰을 직접 전달하는 것이 아니라, 클라이언트는 액세스 토큰을 요청할 때 인증 서버에 POST 요청을 보내고, 인증 서버는 액세스 토큰을 JSON 형식의 응답 바디에 담아서 클라이언트에게 반환한다.

POST /token HTTP/1.1
Host: authorization-server.com
Content-Type: application/x-www-form-urlencoded

grant_type=authorization_code&
code=SplxlOBeZQQYbYS6WxSbIA&
redirect_uri=https%3A%2F%2Fclient%2Eexample%2Ecom%2Fcb&
client_id=CLIENT_ID&
client_secret=CLIENT_SECRET

엑세스 토큰 요청

HTTP/1.1 200 OK
Content-Type: application/json;charset=UTF-8
Cache-Control: no-store
Pragma: no-cache

{
  "access_token":"2YotnFZFEjr1zCsicMWpAA",
  "token_type":"Bearer",
  "expires_in":3600,
  "refresh_token":"tGzv3JOkF0XG5Qx2TlKWIA",
  "example_parameter":"example_value"
}

엑세스 토큰 응답
JSON 형식으로 액세스 토큰을 응답하고, HTTPS를 사용하여 통신을 암호화하며, 클라이언트에게는 HTTPS로 액세스 토큰을 요청하는 API를 제공한다. 이렇게 하면 액세스 토큰을 안전하게 전달할 수 있다.

물론..
HTTPS 프로토콜을 사용하더라도, 요청과 응답을 중간에서 가로채는 공격인 "중간자 공격"이 있을 수 있긴 하다. -> OAuth2 의 허점 중 하나이기도 하다.

참고
https://yelimkim98.tistory.com/45

인증의 세밀화

항상 헷갈렸던 부분은 인증 서버에서의 인증의 종류와 리소스 서버에서의 인증을 잘 구별하는 것이었다.

SSO 로그인을 구현하는 경우,
인증 서버에서는 Authorization Code를 발급할 때 총 2가지 인증 절차를 거친다.
서버가 분리되고 각각의 역할을 분리해줄 수록, 중간자 공격같은 것이 일어날 수 있기 때문에 각 서버에서 request로 받은 정보들에 대해 계속 인증을 해주는 것이다.
아키텍처에서 요소들을 분리할 수록, 전송 과정에서 공격이 일어나기 쉽기 때문에 인증 과정이 필요한 것이다.

  1. 사용자가 인증 서버에 로그인하면, 인증 서버는 해당 사용자가 실제로 존재하고 인증되었음을 확인
  2. 클라이언트 애플리케이션에 대한 승인 정보도 함께 확인

즉, 로그인한 사용자에 대한 인증 절차와 인증 서버가 클라이언트 애플리케이션에 부여한 승인 정보를 확인해주는 두 가지 절차를 거치는 것이다. 그래서 클라이언트는 처음에 인증 서버로 리디렉션될 때 client_id라는 애플리케이션 자체를 식별하는 클라이언트 ID 값을 전송하게 된다.
그러면 인증 서버는 애플리케이션을 식별해서 승인해준 애플리케이션에서의 요청인지 확인해주는 것이다.

만약 SSO 로그인이 아니라면,
1. 클라이언트 애플리케이션에서 사용자 정보를 가지고 인증을 해준다.
2. 그리고 인증 서버에서는 클라이언트 애플리케이션에 대한 승인 정보를 확인해주는 것이다.

리소스 서버에서의 인증 절차도 꽤나 까다롭다. 인증서버와 리소스 서버에서의 인증 절차의 차이점을 깨닫고 어디서 사용자 정보를 가져오는지 파악하는 것도 쉽지 않았다.

리소스 서버에서의 인증 절차는 인증 서버가 클라이언트에게 준 Access Token에 대한 인증을 거친다. 이 Access Token을 검증해주고자 두 가지 방식으로 구현할 수 있는데,

  1. 인증 서버 직접 호출
  • 리소스 서버가 토큰을 검증해야 할 때 토큰을 발행한 권한 부여 서버를 직접 호출한다.
    그러나 인증 서버에 불필요한 부담이 생길 수가 있고 종속성이 생긴다는 단저dl dlTek.
  1. 공유된 데이터베이스 이용
  • 인증 서버와 리소스 서버가 직접 통신할 필요가 없다.
    또한 인증 서버를 재시작하거나 중단해도 권한 부여가 작동한다는 장점이 있다.
    그러나 공유된 데이터베이스가 병목 지점이 되고 시스템 성능에 영향을 미칠 수 있다는 단점이 있다.

구현했던 프로젝트에서는 인증 서버를 직접 호출하는 방식을 택했는데, 공유된 데이터베이스를 이용해주는 방법으로 구현할 때 어떻게 병목을 줄일 수 있을지, 또는 효과적인 쿼리로 사용자를 조회해줄 수 있을지 고민해보면 좋을 것 같다.

JWT 토큰을 이용한다면 사용자 인증에 위변조만 확인해주면 되는가?

OAuth2의 약점은 무엇이 있을까? (feat. 공격의 종류)

profile
그게, 할 수 있다고 믿어야 해

0개의 댓글