OAuth2 프레임워크의 주 목적은 타사 웹사이트나 웹이 리소스에 접근할 수 있게 허용하는 것이다.
기본적으로 사용해온 HTTP Basic 인증 방식은 다음의 두 특징을 가진다.
이 두 특징에서 발생할 수 있는 발생 가능한 보안 상 문제점에는 다음의 것들이 있다.
위 두 가지는 보안 약화에 일조하므로 애플리케이션 아키텍처에서는 제거하는 것이 좋고, 많은 경우 별도의 시스템에서 사용자 자격 증명을 관리하는 것이 낫다. 한 조직에서 이용하는 모든 앱의 자격 증명을 모두 따로 구성, 이용하게, 즉 각 앱이 별도로 자격 증명을 관리하게 할 수도 있다. 하지만 이는 관리 상의 어려움을 가진다.
자격 증명 관리의 책임을 시스템 구성 요소에 격리하고, 이 구성 요소를 권한 부여 서버라 하자. 이와 같은 권한 부여 서버를 구성하면 같은 사용자를 나타내는 중복된 자격 증명이 제거되고, 아키텍처도 간소화되며 유지 관리도 쉬워진다.
OAuth2의 구성 요소에는 다음의 것들이 있다.
OAuth2를 이용한다는 것은 권한 부여에 토큰을 이용한다는 것이다. 토큰을 얻은 후에는 특정 리소스에 접근 가능해지는데, OAuth2는 그랜트(grant)라고 하는 토큰을 얻는 여러 방법을 제공한다.
아래는 일반적인 그랜트 유형들이다. 구현을 하려면 그랜트를 선택하고, 그랜트 유형별로 토큰 생성 방식을 알아야 하며, 앱의 요구 사항에 따라 그랜트 유형들 중 하나를 선택해야 한다.
클라이언트는 사용자가 인증해야 하는 권한 부여 서버의 엔드포인트로 사용자를 리디렉션, 사용자가 자격 증명을 입력할 수 있는 권한 부여서버의 로그인 양식이 있는 페이지를 연다. 기술적으로는 사용자를 권한 부여 서버로 리디렉션할 때, 클라이언트는 다음의 세부 정보가 포함된 요청 쿼리로 권한 부여 엔드포인트를 호출한다.
response_type
클라이언트가 코드를 기대함을 권한 부여 서버에 알리는 값인 code
를 포함client_id
앱 자체를 식별하는 클라이언트 ID 값redirect_uri
인증 성공 후 사용자를 리디렉션할 위치를 권한 부여 서버에 알려줌.scope
허가 권한state
CSRF 보호를 위한 CSRF 토큰을 정의인증에 성공하면 권한 부여 서버는 리디렉션 URI로 클라이언트를 재호출, 코드와 상태 값을 제공한다. 클라이언트는 상태 값이 요청에 보낸 것과 같은지를 검사, 다른 사람이 해당 URI를 호출하는 것이 아닌지를 확인하고, 코드를 이용해 다음 단계에서 액세스 토큰을 얻는다.
1단계에서 생성된 코드는 사용자가 리소스에 접근할 수 있도록 사용자가 인증했다는 클라이언트의 증명이다. 이후 클라이언트는 토큰을 얻기 위해 코드로 권한 부여 서버를 호출한다.
❓권한 부여 서버가 왜 바로 액세스 토큰을 주지 않을까?
권한 부여 서버가 실제로 올바른 클라이언트에서 받았는지 확인하지 않은 액세스 토큰으로 리디렉션 URI를 곧바로 호출하는 것은 보안 상 취약하다. 위 1단계는 사용자-권한 부여 서버 간의 상호 작용이므로 클라이언트는 다시 자신이 적합한 클라이언트임을 증명해야 할 필요가 있다.
클라이언트는 액세스 토큰을 얻기 위해 다음 사항을 제시하고 호출한다.
code
1단계에서 받은 승인 코드. 사용자가 인증받았음을 증명client_id, client_secret
클라이언트 자격 증명redirect_uri
1단계에서 사용된 것과 같음grant_type
현재 실행된 인증 흐름이 무엇인지 지정. 여기선 authorization_code
값을 가짐.서버는 요청에 대한 응답으로 access_token
을 반환한다. 이 토큰은 클라이언트가 리소스 서버의 리소스를 호출하는 데 사용할 수 있다.
클라이언트는 리소스 서버의 엔드포인트를 호출할 때 권한 부여 요청 헤더에 액세스 토큰을 담아 사용한다.
승인 코드 그랜트 유형은 사용자가 자신의 자격 증명을 클라이언트와 공유하지 않고도 클라이언트가 작업을 하도록 허가할 수 있다는 점에서 장점을 가진다. 하지만 승인 코드를 가로채는 경우에는 클라이언트 자체 자격 증명으로 인증을 해야 하고, 이 자격 증명 또한 도난 당할 가능성도 없지는 않다. 비록 이런 일이 실제로 일어날 가능성이 높지는 않지만 취약성을 고려해 볼 필요는 있다.
리소스 소유자 자격 증명 그랜트 유형이라고도 한다. 클라이언트가 사용자 자격 증명을 수집하고 이것으로 인증하고 권한 부여 서버에서 액세스 토큰을 얻는다. 클라이언트와 권한 부여 서버를 같은 조직에서 구축하고 유지 관리할 때만 이용한다.
앱이 사용자에게 로그인 양식을 보여주고 클라이언트가 서버에 자격 증명을 보내 인증 과정을 처리한다. 사용자는 앱에서 인증 책임을 어떻게 설계했는지 알 필요 없다.
클라이언트는 사용자 자격 증명을 수집하고 권한 부여 서버를 호출해 액세스 토큰 얻는다. 요청에는 다음 정보들이 포함된다.
grant_type
여기서는 password
client_id, client_secret
클라이언트가 자신을 인증하기 위한 자격 증명scope
허가 권한username, password
사용자 자격 증명. 일반 텍스트 형식으로 요청 헤더의 값으로 전송.위 요청에 대한 응답으로 클라이언트는 액세스 토큰을 받으며, 이 액세스 토큰으로 리소스 서버의 엔드포인트를 호출한다.
클라이언트는 권한 부여 헤더에 액세스 토큰을 추가하고, 이 토큰으로 리소스 서버의 엔트포인트를 호출한다.
사용자 자격 증명을 클라이언트 앱과 공유하므로 승인 코드 부여 유형보다 덜 안전하지만, 보다 직관적이기는 하다. 실제 시나리오에서는 이 그랜트 유형은 피하는 것이 좋고, 기껏해야 차선으로 생각하는 것이 좋다.
OAuth2가 지원하는 가장 단순한 그랜트 유형으로, 사용자가 관여하지 않을 때, 두 앱 간의 인증을 구현할 때 이용 가능하다. 외부 서버가 인증할 수 있게 하고 우리 서버가 노출하는 특정 리소스를 호출할 수 있게 해야 하는 경우에 사용한다.
전체적으로는 암호 그랜트 유형과 비슷하지만, 액세스 토큰 요청에 사용자 자격 증명이 필요하지는 않다.
다음 요청을 보냄
grant_type
여기서는 client_credentials
client_id, client_secret
scope
응답으로 액세스 토큰을 받고, 이를 이용해 리소스 서버의 엔드포인트 호출한다.
권한 부여 요청 헤더에 액세스 토큰을 추가하고 이 토큰으로 리소스 서버의 엔드포인트를 호출한다.
OAuth2는 토큰이 어떻게 구현되느냐에 대해서는 관여하지 않지만, 토큰은 가능한 최소한의 수명을 가지도록 해야 한다. 만약 토큰을 만료되지 않는 것으로 구현한다면 클라이언트가 같은 토큰으로 리소스 서버의 리소스를 제한 없이 호출 가능해지고, 만약 이 토큰이 분실된다면 누구든 마음껏 리소스에 접근할 수 있게 된다. 만료되지 않는 토큰은 사용자 자격 증명만큼이나 강력하다.
갱신 토큰은 새 액세스 토큰을 얻기 위해 자격 증명을 이용하는 방법의 대안이다. 이 갱신 토큰을 가지고서 클라이언트는 이용된 그랜트 유형에 따라새 액세스 토큰을 얻기 위한 흐름을 다시 실행할 수 있다.
권한 부여 서버는 재인증 필요 없이, 액세스 토큰과는 값과 용도가 다른 갱신 토큰을 발행할 수 있고, 클라이언트는 갱신 토큰으로 재인증 없이 새 액세스 토큰을 얻을 수 있다.
만약 암호 그랜트 유형에서 갱신 토큰을 이용하지 않는다면, 토큰이 만료됐을 때 사용자에게 재인증을 요청하거나 사용자의 자격 증명을 저장해야 한다. 어느 쪽이든 자격 증명의 노출 가능성이 높아지는 것은 동일하다.
한편 갱신 토큰을 이용하면, 자격 증명을 저장하거나 매번 사용자를 리디렉션할 필요 없이 갱신 토큰을 저장하고 필요할 때 이로써 새 액세스 토큰을 얻을 수 있다. 갱신 토큰은 노출이 확인되면 취소할 수도 있다.
권한 부여 서버는 승인 코드나 암호 그랜트 유형에서 액세스 토큰을 발행할 때 함께 갱신 토큰을 반환한다. 클라이언트 자격 증명에서는 사용자 자격 증명이 필요 없으므로 갱신 토큰도 사용되지 않는다.
액세스 토큰이 만료되면 다음 세부 정보가 포함된 요청을 발행한다.
grant_type
refresh_tokenrefresh_token
갱신 토큰의 값을 가진다.client_id, client_secret
scope
. 더 많은 허가 권한 부여 시에는 재인증이 필요.요청에 대한 응답으로는 새 액세스 토큰과 새 갱신 토큰을 발행한다.
취약성은 프레임워크 위에 기능을 잘못 구현한 결과이며, 스프링 시큐리티를 이용하는 것만으로 이 대부분의 취약성은 완화될 수 있다.