웹 API를 보호하기 위해 사용되는 보안 프로토콜인 OAuth 2.0 에 대해 자세히 설명하는 책
- OAuth: 자신이 소유한 리소스에 어플리케이션이 접근할 수 있도록 허용하도록 접근 권한을 위임해주는 프로토콜
- 시스템의 구성 요소가 다른 시스템의 구성 요소에 대한 접근 권한을 허용하도록 하는 인가 프레임워크이다
- 과거에 많이 쓰인 자격 증명 공유(ID, PASS 방식) 을 탈피하기 위해 사용된다
- 주요 용어
- 리소스 소유자: 사용하는 당사자 ( 주로 사람 )
- 클라이언트: 리소스 소유자로부터 권한을 위임받고자 하는 어플리케이션
- 인가 서버: 보안 자격 증명인 토큰을 발급하는 서버
- 인가, 토큰 엔드 포인트와 같은 인가, 인증 관련 기능을 가진다
- 보호된 리소스: 권한이 필요한 웹 API 컴포넌트
- 클라이언트 자격 증명: 주로 client_id , secret로 이루어져 Http 인가 헤더로 주로 전달한다
- 인가 서버에서 클라이언트에게 발급하여 사용한다
- 액세스 토큰: 클라이언트에게 권한이 위임됨을 나타내기 위해 인가 서버가 발급하는 토큰
- 인가 서버와 보호된 리소스는 토큰을 이해해야 하나 클라이언트는 불필요
- 범위: 보호된 리소스에 대한 접근 권한으로 공백으로 구분된 문자열 조합으로 표현
- 리프레시 토큰: 새로운 액세스 토큰을 얻기 위한 토큰
- 리소스 소유자가 없는 경우 다시 접근 권한을 요청하기 위해 사용
- 특징
- 인증 프로토콜이 아닌 인가 프로토콜이다 → 인증은 Open ID Connect
- 토큰의 포맷은 자유 → JWT, Token Introspection 을 주로 이용한다
- 암호화 방법 역시 자유: JOSE 와 JWK 같은 암호 메커니즘을 주로 이용한다
- 구성원과 구성 요소 간 상호 작용 방식
- 백 채널 방식: 리소스 소유자 밖에서 이루어지는 직접 OAuth 통신
- 프론트 채널 방식: 중간에 웹 브라우저를 통한 HTTP 리다이렉트를 이용한 간접 통신
- 인가 그랜트(절차) 종류 및 플로우
- 인가 코드 그랜트 ( grant-type: authorization_code )
- 임시 자격 증명으로 인가 코드를 사용하는 방식
- 프론트 + 백 채널을 사용하는 가장 기본적인 방식
- 인가 코드는 한 번 이상 사용 가능해서는 안된다
- 작동 순서
- 클라이언트가 소유자를 인가 엔드 포인트로 리다이렉트시켜 본인 인증한다
- 인증된 소유자가 클라이언트를 인가하면 클라이언트로 리다이렉트하면서 인가 코드를 전달한다
- 클라이언트는 인가 코드와 클라이언트 자격 증명을 토큰 엔드 포인트에 전달한다
- 인가 서버는 액세스 토큰을 클라이언트에게 전달한다
- 클라이언트는 액세스 토큰으로 리소스에 접근한다
암시적(implicit) 그랜트 ( grant-type: implicit )
- 클라이언트가 웹 브라우저 내부에서 동작할 때 사용하는 방식
- 프론트 채널만을 사용하는 그랜트 타입
- 리프레시 토큰을 얻을 수 없다 → 브라우저를 사용하는 것은 소유자라서 사실 상 필요하지 않다
- 작동 순서
- 클라이언트가 인가 엔드 포인트에 접근하여 본인 인증한다
- 인가 서버는 토큰을 즉시 생성하여 클라이언트에게 제공한다
- 클라이언트는 액세스 토큰으로 리소스에 접근한다
- 클라이언트 자격 증명 그랜트 ( grant-type: client_credentials )
- 명시적인 리소스 소유자가 없는 경우 사용하는 방식 ( ex: 서버끼리 통신 )
- 리프레시 토큰을 얻을 수 없다 → 서버가 새로 토큰을 받으면되서 사실 상 필요하지 않다
- 작동 순서
- 클라이언트가 client_id, secret 으로 토큰 엔드 포인트에 토큰을 요청한다
- 인가 서버는 액세스 토큰을 발급한다
- 클라이언트는 액세스 토큰으로 리소스에 접근한다
리소스 소유자 자격 증명 그랜트 ( grant-type: password)
- 인가 서버가 사용자, 비밀번호는 알고 있는 경우 클라이언트가 해당 정보를 리소스 소유자에게 요청하여 액세스 토큰과 교환하는 방식
- 안티 패턴이나 리다이렉트는 할 수 없는 경우 등, 다른 대안책이 없는 경우 사용한다
- 어설션 그랜트
- 클라이언트가 토큰을 교환하는 과정에서 어설션이라는 구조화되고 암호화된 항목을 사용하는 방식
- 백 채널만 이용하며 표준 포맷으로는 SAML 또는 JWT를 사용한다
- 주로 클라이언트가 신뢰할 수 있는 서드 파티인 경우 사용된다
- **PKCE-**enhanced Authorization Code Grant Type
- 인가 요청과 토큰 요청을 바인딩하여 인가 코드 탈취 공격을 방어하도록 만들어진 확장 그랜트
- 방식
- 클라이언트는 code_verifier를 생성하고 code_challenge_method를 기반으로 code_challenge를 계산한다
- 인가 서버에 code_challenge 와 code_challenge_method를 전달하고 서버는 이를 저장 후 인가 코드를 전달한다
- 클라이언트는 인가 코드와 code_verifier를 토큰 엔드 포인트에 전달한다
- 인가 서버는 전달받은 code_verifier와 code_challenge_method로 code_challenge를 계산하고 일치하는지 확인한다
- 클라이언트 시크릿
- 인가 서버에서 클라이언트 소프트웨어 자체를 인증하기 위한 목적으로 쓰이는 값
- 공개 클라이언트
- 클라이언트 시크릿을 가질 수 없는 클라이언트
- 액세스/리프레시 토큰, 인가코드만을 가질 수 있다
- 기밀 클라이언트
- 설정 기간을 가진 클라이언트 시크릿을 가질 수 있는 클라이언트
- 웹 서버가 client_secret 을 가지기 때문에 백 채널로만 전달되는 secret을 브라우저를 통해 획득할 수 없다 → 브라우저는 client_id를 획득 할 수 있는 방법이 존재하긴 함
- 보안 취약점
- CSRF 공격 방지: state 파라미터( 구조가 정해지지 않은 값) 사용으로 인가 서버가 클라이언트로 리다이렉트 할 때 이 값을 전달한다
- 클라이언트 자격 증명 탈취: 웹 브라우저같이 client_secret 탈취가 쉬운 경우 동적 클라이언트 등록을 이용한다
- 리다이렉트 URL 등록: 최대한 구체적으로 등록하여 사용하도록 해야한다 → 하위 도메인 허용 금지
- 파라미터로 액세스 토큰을 사용하지 말자
- 보안 헤더를 사용하자
- MIME 스니핑 방지 → X-Content-Type-Options: nosniff
- XSS 필터 활성화 → X-XSS-Protection: 1, mode=block
- Http 사용 방지 → Strict-Transport-Security
- OAuth 토큰
- Bearer 토큰: 시크릿이나 시그니처를 포함하지 않은 평문 토큰
- 최소한의 권한 범위를 지정해야 탈취에도 안전해진다
- 서버는 액세스 토큰의 해시값을 저장하여 탈취시에도 안전하다
- JWT 토큰: 주로 Base64로 인코딩된 JSON 객체를 이용하는 토큰
- 토큰 자체만으로 필요한 정보를 가져 불필요한 통신을 제거
- 표준 키가 존재하여 다른 용도로 사용하면 안된다
- JOSE: JWT 를 보호하기 위한 암호화로 서명을 위한 JWS와 암호화를 위한 JWE로 구성되어있다
- 토큰 인트로스펙션
- 클라이언트 동적 등록
- Open Id Connect ( 사용자 인증 )
- 확장 프로토콜 및 토큰