Programming-[Backend]/Keycloak

Keycloak - 5. 보안

컴퓨터 탐험가 찰리 2024. 8. 1. 11:24
728x90
반응형

KeyCIoak - 모던 애플리케이션을 위한 ID 및 접근관리 | 에이콘출판(주) | 스티안 토르거센, 페드로 이고르 실바 지음, 최만균 옮김

서적을 참고하여 요약한 시리즈 글이다.

 


 

 

1. 내부 및 외부 애플리케이션 이해

 

애플리케이션을 보호할 때 가장 먼저 고려할 사항은 애플리케이션이 내부인지 외부인지 확인하는 것이다.

 

1.1 Consent Required 옵션

내부 애플리케이션은 기업이 자체 개발한 애플리케이션으로 신뢰할 수 있다. Keycloak에 애플리케이션을 등록한 관리자가 사용자를 대신해서 접근 권한을 사전 승인할 수 있다. 따라서 사용자에게 애플리케이션에 대한 접근 권한을 부여하도록 따로 요청할 필요가 없다. Consent Required 옵션을 비활성화해도 된다. 반면 외부 애플리케이션의 경우 사용자가 인지할 수 있도록 consent required 옵션을 켜고 scope가 바뀔 때마다 사용자에게 동의를 받아야한다.

 

 

2. 웹 애플리케이션 보호

 

2.1 아키텍처 고려사항

웹 애플리케이션의 보호를 위해 고려해야할 사항은 다음 두 가지다.

  • 전통적인 웹 애플리케이션인지, 브라우저에서 실행되는 최신 SPA(Single Page Application)인지 구분이 필요하다.
  • 애플리케이션이 REST API를 사용하는지, 사용한다면 내부용인지 외부용인지 구분이 필요하다.

또한 SPA인 경우 다음 네 가지 경우에 따라 고려해야할 사항이 조금씩 다르다.

  • Server side: 웹 서버 내부 또는 애플리케이션 서버 내부에서 실행되는 경우
  • SPA with dedicated REST API: 브라우저에서 실행되거나 전용 도메인의 API만 호출하는 경우
  • SPA with intermediary API: 동일한 도메인에서 호스팅되는 중개 API를 통해서만 외부 REST API를 호출하는 경우
  • SPA with external API: 다른 도메인에서 실행되는 API를 호출하는 경우

 

2.2 공통적인 보안 사항

아키텍처와 상관없이 공통적으로 지켜야할 보안 사항은 다음과 같다.

  1. PKCE(Proof Key for Code Exchange) 확장과 함께 인가 코드 흐름(Authorization code flow)를 사용하는 것
  2. 애플리케이션에서 사용자 자격 증명을 수집하지 않고 기존 프로그램의 로그인 페이지를 유지하지 않는다.
    1. 애플리케이션이 손상된 경우 공격받을 수 있다.
    2. two-factor authentication과 같은 강력한 기능을 사용하지 못할 수 있다.
    3. SSO 및 소셜 로그인과 같은 Keycloak의 장점을 이용할 수 없다.
  3. 애플리케이션에 Keycloak의 로그인 페이지를 iframe으로 넣지 않는다.
    1. 애플리케이션의 취약점의 영향을 keycloak이 받게 된다.
    2. 사용자가 로그인 페이지의 출처를 신뢰하기 어렵게 된다. 브라우저에서 서드파티 쿠키를 차단 당하는 경우 keycloak 로그인 페이지가 작동하는데 필요한 쿠키에 접근하지 못할 수도 있다.

 

결국, 사용자가 신뢰할 수 있는 google, amazon, keycloak 등과 같은 페이지로 이동하고, 애플리케이션은 거기서 authorization code를 받고 redirect 하는 방식으로 인가 코드 흐름을 사용해야한다.

 

 

2.3 서버 사이드 웹 애플리케이션 보호

 

서버 사이드 웹 애플리케이션을 보호하는 방법에 대해 알아본다.

  1. Keycloak에 보안 클라이언트(confidential client)를 등록한다. 보안된 클라이언트를 사용하면 공격자가 유출된 인가 코드를 얻더라도 사용할 수 없게 된다.
  2. PKCE 확장을 활용하는 것이 좋다. 여러 유형의 공격에 대한 보호를 제공한다.
  3. 클라이언트에 적절한 redirect URI를 설정하고 open redirect를 하지 않도록 해야한다. 공격자가 https://trusted-site.com/....?redirect_url=http://attacker.com 방식으로 리다이렉트되는 사이트를 조작하는 취약점이 발생할 수 있다.

 

2.4 전용 REST API가 포함된 SPA 보호

위 서버 사이드 웹 애플리케이션의 보호와 마찬가지로 Keycloak을 사용하고 보안 클라이언트의 인가 코드 흐름을 사용해야한다.

 

2.5 중개 REST API가 포함된 SPA 보호

 

SPA와 동일한 도메인에서 호스팅되는 중개 API를 사용하면 보안 클라이언트를 활용할 수 있고 브라우저에서 직접 토큰에 접근할 수 없기 때문에 토큰이 유출될 위험이 줄어든다.

 

또한 기본적으로 CORS 설정이 되어있기 때문에 브라우저가 중개 API 외 다른 REST API를 호출하는 것을 허용하지 않는다. 해당 SPA는 동일한 도메인에서의 중개 REST API를 활용하기 때문에 CORS를 따로 처리할 필요도 없다.

 

 

2.6 외부 REST API가 포함된 SPA 보호

 

SPA 자체에서 Keycloak이 제공하는 인가 코드 흐름을 수행하는 것이 가장 안전하다. 다만 브라우저에 토큰이 노출되기 때문에 보안 수준이 높다고는 할 수 없다. 금융 애플리케이션과 같이 민감한 경우에는 이런 방식을 권장하지 않는다. 보안 수준을 조금 높여줄 수 있는 활용 가능한 기법들이 있다.

  • 리프레시 토큰의 만료 시간을 짧게 설정한다. Keycloak 클라이언트의 session timeouts에서 설정할 수 있다. 이 설정을 수행하면 리프레시 토큰을 30분 동안만 유효하게 할 수 있고, SSO 세션은 보통 며칠 동안 유효하게 된다.
  • 리프레시 토큰을 갱신한다. Keycloak의 realm에서 Revoke Refresh Token을 활성화하여 이전에 사용한 리프레스 토큰은 폐기하도록 한다. 따라서 SPA 또는 공격자가 리프레시 토큰을 사용할려고 하면 즉시 리프레스 토큰이 무효화된다.
  • PKCE 확장을 사용한다.
  • 토큰을 windows state 또는 HTML5 스토리지에 저장하고 window.sessionStorage.accessToken과 같이 유추가 쉬운 키를 사용하지 않아야한다.
  • OWSAP(Open Web Application Security Project)의 Best Practice를 활용한다.
  • 애플리케이션에서 서드파티 스크립트를 사용하는 경우 면밀하게 검증한다.

 

3. 네이티브 및 모바일 애플리케이션 보호

 

3.1 애플리케이션 보호 방식과 종류

모바일 애플리케이션 자체에서 로그인 페이지를 구현하여 사용자의 이름과 패스워드 등 인증 정보를 수집하고 OAuth 2.0 리소스 소유자 패스워드 자격 증명 부여(Resource Owner Password Credential grant)를 활용해 토큰을 획득하는 방법은 권장하지 않는다. 앞서 웹 부분에서 언급한대로 애플리케이션이 직접 사용자 자격증명에 접근하는 것 자체가 금지되는 사항이며, Keycloak이 제공하는 다양한 기능들을 사용할 수 없기 때문이다.

 

즉 애플리케이션들이 Keycloak을 사용해야하고 이는 Keycloak 인증을 위해 브라우저를 사용해야함을 의미한다. 가능한 방법은 다음과 같다.

  • 임베디드 뷰 사용 (웹에서의 iframe과 마찬가지로 비권장)
  • 외부 사용자 에이전트 사용(사용자의 기본 브라우저)
  • 안드로이드 및 iOS와 같은 플랫폼에서 지원하는, 애플리케이션이 필요없는 인앱 브라우저 탭 사용

 

인앱 브라우저 페이지도 악의적인 사용자가 로그인 페이지를 렌더링하여 사용자의 자격증명을 탈취할 수도 있다. 사용자가 외부 브라우저에서 로그인 페이지로 접속하게 하는 방법이 가장 권장된다. 다음 다이어그램과 같은 방식으로 인가가 진행된다.

 

 

 

인가 코드를 애플리케이션에 주는 경우, OAuth 2.0에서 정의한 특수한 리다이렉트 URI를 사용하는 4가지 방법이 존재한다.

 

  • Claimed HTTPS scheme: 애플리케이션이 HTTPS 구조의 URI를 요청하고, 브라우저가 아닌 애플리케이션 자체에서 URI에 접속하는 콜백 방식이다.
  • Custom URI Scheme: 사용자 지정 URI 구조를 애플리케이션에 등록한다. Keycloak이 등록한 사용자 지정 URI로 리다이렉트하면 애플리케이션에 요청이 전송된다. 여기서 URI 구조는 애플리케이션 개발자가 소유한 도메인의 역순과 일치해야한다. org.example.app://oauth2/provider/의 리다이렉트 URI는 app.example.org의 도메인 이름과 일치해야한다.
  • Loopback interface: 애플리케이션이 임시 웹 서버의 임의의 포트를 오픈하여 여기서 URI를 등록한 뒤 애플리케이션 웹 서버에 요청을 보낸다.
  • A special redirect URI: 특수한 urn:ietf:wg:oauth:2.0:oob 리다이렉트 URI를 사용하면 인가 코드가 Keycloak에 표시된다. 이에 따라 사용자가 수동으로 인가 코드에 복사하여 애플리케이션에 붙여넣을 수 있다.

HTTPS가 안전하기 때문에 사용하는 것이 권장되지만, HTTPS 구조를 사용할 수 없는 경우에는 루프백 인터페이스 활용이 권장된다.

 

3.2 실습

 

ch6 코드를 사용한다. 애플리케이션을 실행하기 전에 Keycloak에 신규 클라이언트로 등록한다.

  • name: cli
  • Access Type: public
  • Standard Flow Enabled: ON
  • Valid Redirect URLs: http://localhost/callback

그리고 이번 실습도 마찬가지로 app.js에서 url path 중 맨 마지막 줄의 open 메서드 내부 '/auth' 부분은 제거해줘야한다.

 

애플리케이션을 실행하면 임의의 port를 갖는 cli가 작동하면서 인가 요청을 수행한다. node 코드상 시스템 브라우저의 open 모듈을 활용하여 다음과 같이 호출하는 코드를 확인할 수 있다.

open('http://localhost:8080/realms/myrealm/protocol/openid-connect/auth?client_id=cli&redirect_uri=http://localhost:' + port + '/callback&response_type=code');

 

코드 호출 및 로그인 시, 인가가 떨어지고 token을 애플리케이션에서 확인할 수 있다.

 

 

 

4. REST API 및 서비스 보호

 

4.1 REST API 보호 및 토큰 전파 방식

keycloak에 의해 보호되는 REST API를 애플리케이션이 호출할려고 하면 keycloak에서 Access Token을 얻어온 다음 REST API로 보내는 요청의 Authorization 헤더에 'bearer eyJh...'의 토큰 값을 실어주면 된다.

 

이런 방식은 마이크로 서비스에서 전체적인 인증 컨텍스트 전파에 유리하다. 다음 그림을 보자.

 

애플리케이션이 서비스 A를 호출할 때 접근 토큰을 포함하여 호출한다. 이후 서비스 A가 같은 접근 토큰을 갖고 서비스 B 및 서비스 C를 호출한다. 모든 서비스가 동일한 인증 컨텍스트를 이용하게 된다.

 

4.2 실습

신규 클라이언트를 생성한다.

  • Client ID: service
  • Client Protocol: openid-connect
  • Access Type: confidential (client authentication 토글을 ON 한다)
  • Standard Flow Enabled: OFF
  • Implicit Flow Enabled: OFF
  • Direct Access Grants Enabled: OFF
  • Service Accounts Enabled(Service Accounts roles 인듯): ON

 

Standard Flow Enabled 옵션을 OFF 처리했기 때문에 인가 코드 흐름을 통해서 토큰을 획득할 수 없다. 하지만 Service Accounts roles를 ON 처리했기 때문에 클라이언트 자격 흐름(Client Credential Flow)를 사용할 수 있다. 이 흐름은 클라이언트의 자격증명을 이용하여 클라이언트를 대신해 토큰을 얻을 수 있는 방식이다.

 

접근 토큰을 획득하기 위해 아래 내용대로 터미널에 입력한다

$ export SECRET=<위에서 작성한 'service' 클라이언트의 secret>
$ curl --data "client_id=service&client_secret=$SECRET&grant_type=client_credentials" http://localhost:8080/realms/myrealm/protocol/openid-connect/token

 

그럼 access token을 얻어오는 것을 확인할 수 있다.

 

728x90
반응형