[MSA스터디] 11. API 접근 보안

vector13·2022년 10월 17일
0
post-thumbnail

오 이번장엔 정상으로 표시되는군
암튼 spring-cloud 안에 authorization-server 프로젝트가 추가 될 예정

OAuth 2.0 및 OpenID Connect 표준 소개

authentication 과 authorization의 차이

  • 인증(authentication ) :사용자 이름과 암호 같은 사용자가 제공한 자격 증명을 확인해 사용자를 식별하는 것

  • 권한 부여(authorization) : 인증된 사용자, 즉 식별된 사용자에게 여러 API에 대한 접근 권한을 부여하는 것

  • OAuth 2.0: 권한 부여를 위한 공개 표준

  • OpenID Connect: 클라이언트 애플리케이션이 권한 부여 서버에서 받은 자격 증명을 기반으로 사용자의 신원을 확인할 수 있도록 OAuth 2.0에 추가된 기능
    OpenID Connect ⊂ OAuth 2.0

OAuth 2.0

사용자에게 권한을 위임받은 3rd-party client application이 사용자를 대신해 보안 리소스에 접근 할 수 있게 하는 것

여기서
• 자원 소유자(resource owner) :==최종 사용자
• client : 최종 사용자의 권한을 위임받아 보안 API를 호출하려는3rd-party 애플리케이션(ex, web app, native mobile app).
• 자원 서버 (resource server ): 보호 대상 자원에 대한 API를 제공하는 서버.
• 권한 부여 서버( authorization server) : 자원 소유자(최종 사용자)를 인증하고 자원 소유자의 승인을 받아서 클라이언트에게 토큰을 발급함. 사용자 정보 관리 및 사용자 인증은 보통 ID 제공자 (==IdP =Identity Provider)에게 위임

권한 부여 서버에 등록된 클라이언트 --> client ID와 client secret(보호 必)을 발급받음 --> 클라이언트는 redirect-URI를 등록 -> 권한 부여 서버는 사용자 인증을 거쳐 발급한 인증 코드(grant code)와 token을 리다이렉트 URI로 전달

접근 토큰엔 시간 제한 有,
권한 부여 서버는 client 애플리케이션에 refresh token을 발급 可 --> client는 이 토큰 이용해 사용자의 관여 없이 접근 토큰을 새로 발급可

  • 권한 코드 승인 흐름(authorization code grant flow)
  1. client application -> 사용자를 권한부여 서버로 보내고 권한 승인 흐름을 시작 using web brwoser
  2. 권한 부여 서버 -> 사용자를 인증 +사 용자의 동의를 요청
  3. 권한 부여 서버 -> 사용자를 인증 코드(grant code)와 함께 client application로 redirect && 권한 부여 서버는 클라이언트가 1단계에서 지정한 redirect URI로 인증 코드를 전송.
  4. client application -> 인증 코드와 접근 토큰을 교환하고자 서버 측
    코드를 사용해 권한 부여 서버를 다시 호출 with client ID와 client secret
  5. 권한 부여 서버 -> 접근 token 발급해 client application로 보냄
    (선택적으로 재발급 토큰을 발급 및 반환 可)
  6. client -> 접근 token 사용해 자원 서버가 공개하는 보안 API에 request 전송
  7. 자원 서버 -> 접근 token 검사&& 검사가 성공 시-> 요청을 처리한다.

이 1 권한 코드 승인 흐름을 포함해 OAuth 2.0 사양에서는 접근 토큰 발급을 위한 권한 승인 흐름을 2 묵시적 승인 흐름 3 자원 소유자 암호 자격 증명 승인 흐름 4 클라이언트 자격 증명 승인 흐름으로 정의하고 있음
이 중 권한 코드 승인 흐름이 가장 안전하지만 가장 복잡한 승인 흐름이다.

OIDC 소개

OIDC (OpenID Connect): 클라이언트 애플리케이션이 사용자의 신원을 확인하게 하려고 OAuth 2.0에 추가된 기능.
OIDC를 사용하면 승인 흐름이 완료된 후--> ID 토큰이 추가된다(==클라이언트 애플리케이션이 권한 부여 서버에서 받아오는 토큰)

ID 토큰 : JWT(JSON Web Token)로 인코딩되며, 사용자 ID, 이메일 주소와 같은 다수의 클레임을 포함. (JSON 웹 서명으로 디지털 서명됨)

OIDC는 디스커버리 엔드포인트(discovery endpoint)를 정의하고 있음
주요 엔드포인트로는 JWKS 엔드포인트(jwks_uri)와 권한 부여 엔드포인트(authorization_endpoing), 사용자 정보 엔드포인트(user-info endpoint)가 있음

시스템 환경을 보호하는 방법에 대한 일반 논의

for )시스템 환경 보호 위해서
1. HTTPS를 사용해 공개된 API에 대한 외부 요청과 응답을 암호화하고 도청을 방지
2. OAuth 2.0 및 OpenID Connect를 사용해 API에 접근하는 사용자 및 클라이언트 애플리케이션에 대한 인증 및 권한 부여를 수행
3. HTTP 기본 인증을 사용해 검색 서비스(넷플릭스 유레카)에 대한 접근을 보호

권한 부여 서버의 모든 외부 통신은 라우팅 by edge server
-> 에지 서버와 product-composite서비스는 OAuth 2.0 자원 서버 역할을 한다.(== 유효한 OAuth 2.0 접근 token 있어야 접근可)

접근 토큰은 서명된 JWT 토큰으로 인코딩되고, 권한 부여 서버는 자원 서버가 토큰의 서명을 검사할 때 사용할 공개 키 (jwk-set)에 접근하도록 엔드포인트를 노출한다고 가정 (b/c 접근 토큰 검사의 오버헤드를 최소화하고자 )

그림으로 보면 이러하다

1. 외부 통신엔 HTTPS를 사용 + 시스템 환경 안에선 plain text HTTP를 사용
2. 외부에선 -> 에지 서버를 거쳐야 로컬 OAuth 2.0 권한부여 서버에 접근가능
3. 에지 서버+ product-composite 마이크로서비스 -> 서명된 JWT 토큰으로 접근 토큰을 검사
4. 에지 서버+ product-composite 마이크로서비스 -> jwk-set 엔드포인트에서가져온 권한 부여 서버의 공개 키로 JWT 기반 접근 토큰의 서명을 검사.

시스템 환경에 권한 부여 서버 추가

스프링 시큐리티 5.1은 권한 부여 서버를 제공하지 않지만, -> 유지보수 모드로 전환된 레거시 프로젝트인 스프링 시큐리티 OAuthSpring Security OAuth에서 쓸 만한 권한 부여 서버를 제공하고 있음

완전한 OpenID Connect 공급자는 아니지만, 로컬 테스트와 자동 테스트를 실행하기엔 충분함

IN) 시큐리티 샘플 프로젝트 : reader 및 writer라는 이름의 OAuth 클라이언트를 구성하고 있다. reader 클라이언트는 read 스코프를 부여받고, writer 클라이언트는 read 및 write 스코프를 부여받는다. 모든 클라이언트는 secret이라는 이름의 시크릿을 가지도록 구성된다.

오.. 없는데..


sample 이 없음 ..

그냥 저자 깃허브 디렉토리 복사하겠음 .. 참내. .

다른 마이크로서비스와 같은 방식으로 유레카 클라이언트를 추가
-> 상태 점검(health) 엔드포인트에 접근할 수 있도록 스프링 부트 액추에이터를 추가 -> 권한 부여 서버를 도커 컨테이너로 실행할 수 있도록 도커파일을 추가 -> 그래들 빌드 파일(spring-security-samples-boot-oauth2authorizationserver.gradle)을 이 책의 소스 코드에서 사용하는 build.gradle 파일과 유사하게 변경. -> sample/AuthorizationServerConfiguration 클래스의 구성을 변경

  • 승인 유형 추가, 스코프 이름 변경, 권한 부여 서버에 등록된 사용자 정보 변경

권한 부여 서버를 시스템 환경에 통합하고자 적용한 변경 사항
1 공통 빌드 파일(settings.gradle)에 권한 부여 서버 추가

2 3개의 도커 컴포즈 파일(docker-compose* yml)에 권한 부여 서버 추가

3 에지 서버(spring-cloud/gateway) 변경 사항
3-1 HealthCheckConfiguration에 권한 부여 서버 상태 점검 추가

3-2 /oauth/로 시작하는 URI 경로 추가

HTTPS로 외부 통신 보호

HTTPS를 사용해 통신을 암호화 하려면
인증서 생성 -> 에지 서버 구성하면된다.
개발 목적의 자체 서명 인증서를 생성하고 -> 인증서를 사용해 HTTPS 기반 외부 트래픽만 허용하도록 에지 서버를 구성하면 됨
keytool -genkeypair -alias localhost -keyalg RSA -keysize 2048 -storetype PKCS12 -keystore edge.p12 -validity 3650

근데 keystore가 어디 생성됐는지 모르겠삼

인증서와 HTTPS를 사용하도록 에지 서버를 구성하고자 게이트웨이 프로젝트의 application.yml 파일에


추가
3개의 도커 컴포즈 파일(docker-compose".yml)에도 포트 및 프로토콜 변경에 따른 변경 사항을 반영

클래스패스를 사용해 인증서를 제공하는 것은 개발 환경에서만 사용해야함

-> 런타임에 자체 서명 인증서를 외부 인증서로 교체하는 방법?

런타임에 자체 서명 인증서 교체

개발 환경에선 자체 서명 인증서를 .jar 파일 안에 두는 것이 유용
런타임 환경에서는 공인된 인증기관CA에서 서명한 인증서를 사용해야 함.
keytool -genkeypair -alias localhost -keyalg RSA -keysize 2048 -storetype PKCS12 -keystore keystore/edge-test.p12 -validity 3650



검색 서비스(넷플릭스 유레카) 접근 보안

HTTP 기본 인증을 사용해 사용자 이름과 암호가 있어야 접근할 수 있도록 검색 서버 (넷플릭스 유레카) API 및 웹 페이지에 대한 접근을 제한

유레카 서버 변경




유레카 클라이언트 변경

유레카 클라이언트에서 참조하는 유레카 서버 연결 URL에 자격 증명을 추가

curl -H "accept:application/json" https://u:p@localhost:8443/eureka/api/apps -ks | jq -r.applications.application[].instance[].instanceId

테스트를 어찌할꼬

뭔가 잘 안되는건가 ..
https://localhost:8443/eureka/web 들어가서 사용자u 비번p 눌러서 로그인하면

근데 책처럼 gateway 밑에 product나 다른것들이 안나옴

OAuth 2.0 및 OpenID Connect를 사용한A PI 접근 인증 및 권한 부여

권한 부여 서버를 추가하면 에지 서버와 product-composite 서비스를 OAuth 2.0 자원 서버로 만들 수 있다

product-composite 서비스에 접근하려면 product:read 스코프 +product:write 스코프 가 추가된 토큰 필요

에지 서버와 product-composite 서비스 변경

product-composite build.gradle에 시큐리티 5.1 의존성 넣고




logAuthorizationInfo : API를 호출할 때마다 관련된 JWT 접근 토큰을 기록

테스트 클래스가 모든 자원에 접근할 수 있도록 허용하고자 TestSecurityConfig 생성

모든 스프링 통합 테스트 클래스 변경


빨간 줄이 난다 했더니만 역시 빌드가 안됨


Mono< void>하고잇는 두 부분에서 안되고 있음 흑..

로컬 권한 부여 서버를 사용한 테스트

전체 보안 컴포넌트를 테스트하며, 로컬 권한 부여 서버를 사용해 접근 토큰을 발급하는 과정인데 이론만 보겠음

도커 이미지를 빌드하고

암호 승인 흐름을 사용해 접근 토큰 획득

curl -k https://writer:secret@localhost:8443/oauth/token -d grant_type=password -d username=magnus -d password=password -s | jq . 통해서
writer 클라이언트를 위한 접근 토큰 (== product:read 및 product:write 스코프가 추가된 접근 토큰을 가져오기

클라이언트 : HTTP 기본 인증을 사용해 로그인, 클라이언트 ID와 시크릿(writer:secret)을 자격 증명으로 사용.
자원 소유자(최종 사용자) : 자격 증명은 username 및 password 매개 변수를 사용해 전송

if) reader 클라이언트를 위한 접근 토큰, 즉 product:read 스코프만 추가된 접근 토큰을 가져 오려면 ? writer --> reader 로 변경

curl -k https://reader:secret@localhost:8443/oauth/token -d grant_type=password -d username=magnus -d password=password -s | jq .

묵시적 승인 흐름을 사용해 접근 토큰 획득

크롬 웹브라우저에서
자체 서명 인증서를 허용하는 웹 브라우저에서 URL을 열고 (https://localhost:8443/oauth/authorize?response_type=token&client_id=reader&redirect_uri=http://my.redirect.uri&scope=product:read&state=48532) reader 클라이언트용 접근 토큰을 얻음 -> 웹 브라우저에서 로그인을 요청하는 메시지가 표시되면 권한 부여 서버 구성에 구성한 자격 증명 입력 -> reader 클라이언트에게 API 호출 권한을 부여

리다이렉트 URL에서 주소 표시줄에 있는 URL에서 access_token 요청 매개 변수의 값 == 접근 토큰 값

? writer 클라이언트용 접근 토큰을 얻으려면
https://localhost:8443/oauth/authorize?response_type=token&client_id=writer&redirect_uri=http://my.redirect.uri&scope=product:read+product:write&state=95372

코드 승인 흐름을 사용해 접근 토큰 획득

코드 승인 흐름은 안전성이 높은 만큼 좀 더 복잡
첫 번째 단계 : 접근 토큰과 교환할 때 사용할 인증 코드를 얻고자 웹 브라우저를 사용
-> code는 웹 브라우저에서 서버 측 코드와 같은 보안 계층으로 전달 + 보안 계층에서는 인증 코드를 접근 토큰으로 교환하려는 새 요청을 권한 부여 서버로 보냄 (cf. 이때 출처를 확인하기 위한 클라이언트 시크릿도 함께 보냄)

? reader 클라이언트용 접근 토큰을 얻으려면
https://localhost:8443/oauth/authorize?response_type=code&client_id=reader&redirect_uri=http://my.redirect.uri&scope=product:read&state=35725 사용
-> http://my.redirect.uri/?code=T2pxvW&state=72489 로 리다이렉트 --> code매개변수에 있는 값이 code값

접근 토큰을 사용해 보안 API 호출

먼저 유효한 접근 토큰 없이 제품 조회 API를 호출

ACCESS_TOKEN=an-invalid-token
curl https://localhost:8443/product-composite/2 -k -H "Authorization: Bearer
$ACCESS_TOKEN" -i

유효하지 않으므로 invalid 출력

? 그럼 아까 발급받은 접근 토큰 사용해 api 호출

ACCESS_TOKEN={a-reader-access-token}
curl https://localhost:8443/product-composite/2 -k -H "Authorization: Bearer
$ACCESS_TOKEN" -i

200 정상 뜸

OpenID Connect 공급자(Auth0)를 사용한 테스트

? 로컬 권한 부여 서버를 인증된 OpenID Connect 공급자로 교체하기 위해선
1 Auth0 사용자 계정과 OAuth 클라이언트 계정 설정
2 Auth0를 OpenID 공급자로 사용하고 실행하는 데 필요한 변경 사항을 적용
3 접근 토큰 획득
3-1 암호 승인 흐름
3-2묵시적 승인 흐름
3-3 권한 코드 승인 흐름
3-4접근 토큰을 사용해 보안 API 호출
3-5 user-into 엔드포인트를 사용해 사용자에 대한 추가 정보 획득

  • Auth0 사용자 계정과 OAuth 클라이언트 계정 설정
    Auth0 계정 만들기 https://auth0.com/
    테넌트tenant 도메인을 만들라는 메시지가 안나왔음 ..

    domain : dev-v3hzyssb.us.auth0.com
  • Auth0를 OpenID 공급자로 사용하고 실행하는 데 필요한 변경 사항을 적용





빌드는 여전히 안됨 .. ^^ 휴..

정리 및 느낀점

11장에서는 에지 서버를 통해 공개되는 API 및 웹 페이지에 대한 접근을
보호하는 방법을 살펴봄.
1 HTTPS를 사용해 외부에서 유입되는 API 접근에 대한 도청을 방지하는 방법
2 OAuth 2.0 및 OpenID Connect를 사용해 사용자 및 클라이언트 애플리케이션의 API 접근을 인증하고 권한을 부여하는 방법
3 HTTP 기본 인증을 사용해 검색 서비스인 넷플릭스 유레카에 대한 접근을 보호하는 방법

휴.. 중간에 빌드가 안되고 자동 테스트(bash통한 테스트) 때문에 실습을 많이 못따라갔음 아쉬움<<짲응

그냥 프로젝트에서 nginx 서버에 SSL 설정 통해서 https 활성화가 아니라 스프링 시큐리티를 사용하면 간단히 HTTPS를 활성화해 외부 도청을 막을 수 있다는 것을 알게됐음 하지만 로컬 권한 부여 서버에서 외부 OPIC 공급자인 auth0로ㅗ 로컬 서버 대체하려는 작업은 또 엄청 복잡한 것처럼 느껴져서 필요성을 못느꼈음

스프링 시큐리티를 이용한 작업은 jwt 이용해서 토큰을 통해 권한 주는 작업까지가 아직은 설득력 있는 작업임 (만일 추가로해야한다면)

이제는 깃허브 코드에 포함되지 않는것들도 생겼는데 이렇게 빌드가 안돼서야 어쩔지 걱정임

profile
HelloWorld! 같은 실수를 반복하지 말기위해 적어두자..

0개의 댓글