gRPC는 고성능 통신을 위한 프레임워크이지만, 보안 없이 사용될 경우 민감한 데이터가 외부에 노출될 수 있다. 특히 마이크로서비스 아키텍처에서 다양한 서비스 간 통신이 빈번히 발생하기 때문에 gRPC 보안 계층(Security Layer)의 적용은 선택이 아닌 필수이다.

 

gRPC의 채널과 스트림의 이해


gRPC는 클라이언트-서버 간의 통신을 위한 추상화된 개념으로, 핵심은 채널(Channel)스트림(Stream)에 기반한다.

 

채널 (Channel)

  • 클라이언트와 서버 사이의 TCP 연결을 설정하고 관리하는 논리적 객체이다.
  • 하나의 채널을 통해 여러 gRPC 호출이 다중 스트림(HTTP/2 기반)으로 이루어진다.
  • 연결 재사용, 로드 밸런싱, TLS 설정 등도 채널에서 담당한다.
ManagedChannel channel = ManagedChannelBuilder.forAddress("localhost", 50051)
    .usePlaintext() // TLS 미사용 (테스트용)
    .build();

 

스트림 (Stream)

  • 채널 위에서 실제 요청/응답이 단방향 또는 양방향으로 흐르는 단위이다.
  • gRPC는 HTTP/2의 멀티플렉싱 기능을 이용하여 하나의 채널 위에 여러 스트림을 동시에 처리할 수 있다.

 


gRPC 보안 계층이란?


gRPC는 HTTP/2 기반에서 동작하며, 보안을 위해 다양한 계층을 설정할 수 있다.

계층 설명
TLS/SSL 데이터 암호화를 통해 중간자 공격(MITH) 방지
서버 인증 서버의 신원을 클라이언트에게 보장
클라이언트 인증 (Mutual TLS) 클라이언트의 신원을 서버가 확인 (양방향 인증)
토큰 기반 인증 (JWT, OAuth2) 인증 및 권한 제어 수행
Interceptor gRPC 내부 요청을 가로채 보안 검사 수행

 


gRPC의 보안 계층: TLS의 이해


🔐 TLS (Transport Layer Security)란?

출처: 나무위키

 

TLS는 인터넷 상에서 안전한 통신을 보장하기 위한 표준 보안 프로토콜이다.

  • 암호화: 클라이언트와 서버 간 전송되는 모든 데이터는 암호화되어 제3자가 내용을 볼 수 없음
  • 무결성: 데이터가 전송 중 변조되지 않았음을 확인 (메시지 인증 코드 사용)
  • 인증: 서버와 클라이언트의 인증서를 통해 신원을 확인하고, 신뢰할 수 있는 대상과만 통신

 

TLS 설정 예시 (서버)
Server server = NettyServerBuilder.forPort(8443)
  .useTransportSecurity(new File("server.crt"), new File("server.key"))
  .addService(new MyGrpcService())
  .build()
  .start();

 


gRPC의 보안 계층: 스트림 암호화


TLS는 단순히 연결만 보호하는 것이 아니라, gRPC에서 스트림 단위로 암호화를 적용한다.

 

 

인증서 교환

  • 서버는 클라이언트에게 자신의 인증서를 전송하고, 클라이언트는 이를 검증한다.
  • Mutual TLS의 경우, 클라이언트도 자신의 인증서를 서버에 전송함으로써 양방향 인증이 가능해진다.

 

 신원 인증 및 키 교환

  • TLS 핸드셰이크 과정에서 RSA, ECDHE 등의 알고리즘을 사용해 암호 키를 안정하게 교환한다.
  • 이후 세션 키 (Session Key)를 사용해 대칭키 방식으로 모든 스트림 데이터를 암호화한다.

 

이로 인해 gRPC의 데이터 스트림은 항상 TLS로 보호되며, 평문으로 노출되지 않는다.

 


gRPC 사용자 인증 종류: Mutual TLS (mTLS, 양방향 인증)


Mutual TLS는 TLS를 기반으로 한 양방향 인증 방식이다. 일반적인 TLS가 서버 인증만 수행하는 반면, 클라이언트와 서버가 서로의 인증서를 검증하는 방식이다.

 

mTLS의 주요 특징

  • 높은 보안성: 서버와 클라이언트 모두의 신원을 확인하므로 안전함
  • 양방향 인증: 상호 간에 신뢰를 보장해야 하는 환경에서 적합 (금융, 사내 인프라)
  • 복잡한 인증서 관리: 인증서 발급/갱신 등의 관리가 필요함
  • 초기 비용: 인증서 기반 인프라 구축 및 설정 필요

 

서버 & 클라이언트 설정 예제
// 서버 설정
Server server = NettyServerBuilder.forPort(8443)
  .useTransportSecurity(new File("server.crt"), new File("server.key"))
  .sslContext(GrpcSslContexts.configure(SslContextBuilder.forServer(...))
      .trustManager(new File("ca.crt"))
      .clientAuth(ClientAuth.REQUIRE))
  .build();

// 클라이언트 설정
ManagedChannel channel = NettyChannelBuilder.forAddress("localhost", 8443)
  .sslContext(GrpcSslContexts.forClient()
      .keyManager(new File("client.crt"), new File("client.key"))
      .trustManager(new File("ca.crt"))
      .build())
  .build();

 

서버도 클라이언트의 인증서에서 서명된 신원을 확인하고 통신을 허용한다.

 


인증 및 인가 (JWT, OAUTH2)


TLS는 통신 채널의 보안(암호화)을 담당하지만, 실제 사용자 인증 및 권한 관리는 애플리케이션 레벨에서 수행해야 한다.

 

JWT 기반 인증 예시 (Interceptor 활용)

gRPC는 요청을 처리하기 전 또는 후에 특정 작업을 가로채 처리할 수 있도록 Interceptor 기능을 제공한다. 인증을 포함한 다양한 기능을 처리할 수 있다.

 

클라이언트는 요청 시 'Authorization: Berear <token>' 형식의 메타데이터를 전송한다.

 

Interceptor의 기능

  • 인증 처리: JWT, OAuth 토큰 검사 등
  • 로깅 및 모니터링: 요청/응답 정보 기록
  • 메시지 변환: 메타데이터 기반 처리
  • 오류 처리: 예외 상황 포착 및 응답 구성

 

Interceptor의 유형

유형 설명
서버 인터셉터 서버 측 요청을 가로채 보안 및 인증 처리 수행
클라이언트 인터셉터 클라이언트 측에서 메타데이터 첨부 및 요청 로깅 등 처리

 

클라이언트 코드
Metadata metadata = new Metadata();
metadata.put(Metadata.Key.of("Authorization", Metadata.ASCII_STRING_MARSHALLER), "Bearer eyJhbGciOi...");

stub.withInterceptors(MetadataUtils.newAttachHeadersInterceptor(metadata));

 

서버 Interceptor
public class AuthInterceptor implements ServerInterceptor {
  @Override
  public <ReqT, RespT> ServerCall.Listener<ReqT> interceptCall(
      ServerCall<ReqT, RespT> call,
      Metadata headers,
      ServerCallHandler<ReqT, RespT> next) {

    String token = headers.get(Metadata.Key.of("Authorization", Metadata.ASCII_STRING_MARSHALLER));

    if (!isValidToken(token)) {
        call.close(Status.UNAUTHENTICATED.withDescription("Invalid or missing token"), headers);
        return new ServerCall.Listener<>() {};
    }
    return next.startCall(call, headers);
  }
}

 

JWT 토큰은 서버에서 유효성 검사 및 사용자 권한(roles, scopes) 기반 인가까지 처리할 수 있다.