Amazon Web Services 한국 블로그

AWS API키 유출 고민없이 안전하게 애플리케이션 코드 작성하기

AWS 클라우드에서 보안은 항상 최우선 순위입니다. AWS 고객은 보안에 가장 민감한 조직의 요구 사항을 충족하도록 구축된 데이터 센터 및 네트워크 아키텍처의 이점을 누릴 수 있습니다. AWS 공동 책임 모델에 따르면, AWS가 호스트 운영 체제 및 가상화 계층에서 물리적 설비 보안을 책임지고, 고객은 게스트 운영 체제(업데이트 및 보안 패치 포함) 및 다른 관련 애플리케이션 소프트웨어를 관리하고 AWS에서 제공한 보안 기능을 구성할 책임이 있습니다.

고객이 직접 관리해야 하는 영역에서 악의적인 공격자가 흔히 AWS API키라고 부르는 AWS 보안 자격 증명을 임의로 취득하여 민감한 데이터를 훔치거나 리소스를 사용하여 많은 비용을 발생시키거나 고객 여러분들의 클라우드 인프라위에서 동작하는 서비스들을 방해 할 수 있습니다.

AWS API에 대한 프로그래밍 방식 액세스를 위한 자격 증명은 액세스 키 ID 및 보안 액세스 키의 형태로 제공됩니다. 액세스 키를 사용하여 AWS CLI 또는 AWS API에 대한 프로그래밍 요청에 서명할 수 있습니다.

액세스 키는 액세스 키 ID (예: AKIAIOSFODNN7EXAMPLE)와 보안 액세스 키 (예: wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY)의 2가지 부분으로 구성됩니다. 사용자 이름 및 암호와 같이 액세스 키 ID와 보안 액세스 키를 함께 사용하여 요청을 인증해야 합니다. 사용자 이름과 암호를 관리하는 것처럼 안전하게 액세스 키를 관리합니다.

따라서, AWS 액세스 자격 증명을 안전하게 유지하는 것이 중요합니다. 애플리케이션 개발자가 클라우드와 AWS 보안 자격 증명을 잘 모르는 상태에서 프로그래밍 코드를 작성하고 애플리케이션을 배포하면 보안 취약성에 노출될 수 있습니다. 이 글에서는 애플리케이션 개발자가 각 개발 환경에서 안전하게 AWS 보안 자격 증명을 사용하는 방법과 AWS Security Token Service (AWS STS)를 사용하여 AWS 액세스 키를 쉽고 안전하게 관리하는 방법을 설명합니다.

Java용 AWS SDK를 사용한 자격 증명 구성 예

자바 애플리케이션을 개발하여 AWS 클라우드 리소스에 배포하려면 AWS 자격 증명을 AWS SDK for Java에 제공해야 합니다. Java 용 AWS SDK에는 하드 코딩된 자격 증명을 대신 사용할 수있는 다양한 자격 증명 공급자 가 포함되어 있습니다. 시스템 속성, 환경 변수, 속성 파일 등을 이용해서 애플리케이션에 자격 증명을 쉽게 삽입할 수 있습니다.

기본적으로 다음 순서대로 자격 증명을 찾습니다.

  1. 환경 변수 : AWS_ACCESS_KEY_ID 와 AWS_SECRET_ACCESS_KEY
  2. Java 시스템 속성 : aws.accessKeyId 와 aws.secretKey
  3. 환경 또는 컨테이너의 웹 자격 증명 토큰 자격 증명
  4. 기본 자격 증명 프로필 파일 : 일반적으로 ~/.aws/credentials(플랫폼마다 다를 수 있음)에 있습니다.
  5. Amazon ECS 컨테이너 자격 증명 : 환경 변수 AWS_CONTAINER_CREDENTIALS_RELATIVE_URI가 설정되어 있으면 Amazon ECS에서 로드됩니다.
  6. 인스턴스 프로파일 자격 증명 : EC2 인스턴스에서 사용되며, Amazon EC2 메타데이터 서비스를 통해 전달됩니다.

이러한 방법들을 이용해 자격 증명을 소스 코드와 분리하여 실수로 자격증명 정보를 체크인하는 위험을 줄일 수 있습니다. 그러나, 개발자가 애플리케이션에서 AWS 서비스를 액세스하기 위해 보안 키를 직접 하드 코딩하는 경우가 있습니다. 예를 들어, 다음은 Amazon S3를 호출하여 객체를 저장하거나 검색하는 애플리케이션의 잘못된 사용 코드입니다.

BasicAWSCredentials awsCreds = new BasicAWSCredentials("access_key_id", "secret_key_id");
AmazonS3 s3Client = AmazonS3ClientBuilder.standard()
                        .withCredentials(new AWSStaticCredentialsProvider(awsCreds))
                        .withRegion(Regions.AP_NORTHEAST_2)
                        .build();

이러한 코드를 실수로 GitHub 퍼블릭 리포지토리에 실수로 커밋할 수 있습니다. 이러한 경우, 악의적인 사용자가 여러분의 AWS 리소스에 손쉽게 접근 가능하고 큰 보안 사고가 일어날 수 있습니다. 따라서 보안 액세스 키 ID 및 보안 액세스 키를 관리하는 모범 사례로 프로그램을 구현하는 것이 매우 중요합니다. (AWS의 액세스 키 ID 및 보안 액세스 비밀 키에 대한 모범 사례와 안티 패턴 에서 확인하실 수 있습니다.)

이제 주요 개발 환경 및 사용 사례에 따라 보안 액세스 키 ID 및 보안 액세스 키를 노출 하지 않고 사용하는 방법을 살펴보겠습니다.

1. AWS IAM 역할로 AWS 자격 증명 설정하기

AWS 내에서 실행되는 애플리케이션의 모범 사례는 바로 AWS Identity and Access Management (IAM) 역할을 사용하는 것입니다. IAM 역할은 특정 권한을 가진 계정에 생성할 수 있는 IAM 자격 증명이며 특정 사용자 또는 그룹과 연결되지 않습니다. 대신 역할을 수임하면 역할 세션을 위한 임시 보안 자격 증명을 제공합니다.

IAM 역할을 활용하면 액세스 키 ID와 보안 액세스를 구성 파일이나 소스 코드에 하드 코딩하지 않고도 AWS 리소스의 액세스 권한을 부여할 수 있습니다. 예를 들어, EC2 인스턴스에 대한 액세스를 정의하는 정책이있는 역할을 연결하여 Amazon Simple Storage Service (Amazon S3) 버킷에 대한 Amazon Elastic Compute Cloud (EC2) 인스턴스 액세스 권한을 부여 할 수 있습니다.

AmazonS3 s3Client = AmazonS3ClientBuilder.standard()
                       .withRegion(Regions.AP_NORTHEAST_2)
                       .build();

2. AWS STS의 임시 자격 증명 사용하기

많은 AWS 고객들이 2개 이상의 AWS 계정을 사용하면서 하나의 계정에서 사용자 자격 증명을 정의하고 그 자격 증명을 사용해 조직에 속한 다른 계정의 AWS 리소스에 액세스하여 사용하고 있습니다.

이런 경우 AWS Security Token Service (AWS STS)를 이용하여 애플리케이션 코드에서 사용할 임시 자격 증명을 요청할 수 있습니다. 이를 통해 신뢰할 수 있는 관계가 있는 IAM 역할을 맡은 다음 역할과 관련된 권한에 따라 시간제한이 있는 임시 자격 증명을 생성할 수 있습니다. 이러한 자격 증명은 유효 기간 동안만 사용할 수 있으므로 위험이 줄어듭니다.

기본 자격 증명 체인이나 특정/사용자 지정 공급자 또는 공급자 체인이 코드에서 작동하지 않는 경우에는 AWS STS를 사용하여 임시 자격 증명을 가져와 자격 증명을 지정합니다. AWS SDK에서 임시 자격 증명을 사용하는 방법에 대한 자세한 내용은 AWS 리소스에서 임시 자격 증명 사용을 참고하세요. AWS STS에서 가져온 임시 자격 증명을 사용하는 경우 BasicSessionCredentials 객체를 생성하여 STS에서 제공한 자격 증명 및 세션 토큰을 전달합니다.

//AWSSecurityTokenServiceClient 객체를 생성합니다.
AWSSecurityTokenService sts_client = new AWSSecurityTokenServiceClientBuilder().standard().withEndpointConfiguration(new AwsClientBuilder.EndpointConfiguration("sts-endpoint.amazonaws.com", "signing-region")).build()

// GetSessionTokenRequest 객체를 생성하고 필요에 따라 임시 자격 증명의 유효 기간(초)을 설정합니다.
GetSessionTokenRequest session_token_request = new GetSessionTokenRequest();
session_token_request.setDurationSeconds(7200); // optional.

// 객체를 사용하여 세션 토큰을 가져오려면 STS 클라이언트에서 getSessionToken를 호출합니다.
GetSessionTokenResult session_token_result =
    sts_client.getSessionToken(session_token_request);

// getSessionToken 호출의 결과를 사용하여 세션 자격 증명을 가져옵니다.     
Credentials session_creds = session_token_result.getCredentials();

//  BasicSessionCredentials 객체를 생성하여 STS에서 제공한 자격 증명 및 세션 토큰을 전달합니다.
BasicSessionCredentials sessionCredentials = new BasicSessionCredentials(
   session_creds.getAccessKeyId(),
   session_creds.getSecretAccessKey(),
   session_creds.getSessionToken());

//  AWS 서비스 클라이언트를 초기화할 수 있습니다.
AmazonS3 s3 = AmazonS3ClientBuilder.standard()
                        .withCredentials(new AWSStaticCredentialsProvider(sessionCredentials))
                        .build();

3. 모바일 애플리케이션에서 Amazon Cognito 사용하기

모바일 애플리케이션의 경우 Amazon Cognito 사용을 권장합니다. Amazon Cognito 는 웹 및 모바일 앱에 대한 인증, 권한 부여 및 사용자 관리를 제공합니다. 직접 사용자 ID와 암호를 사용하여 직접 로그인하거나 Facebook, Amazon, Google 또는 Apple 같은 제3자 인증을 통해 로그인할 수 있도록 구성할 수 있습니다.

Amazon Cognito와 함께 AWS iOS용 Mobile SDK, AWS Android 및 Fire OS용 Mobile SDK를 사용하여 사용자 고유 자격 증명을 만들고 AWS 리소스에 대한 보안 액세스를 인증할 수 있습니다. AWS Amplify 프레임워크를 이용하면 Amazon Cognito를 웹 또는 모바일 앱과 쉽게 통합할 수 있습니다. 다음은 Flutter와 AWS Amplify를 이용한 예제입니다.

void loginWithCredentials(AuthCredentials credentials) async {
 try {
   final result = await Amplify.Auth.signIn(
       username: credentials.username, 
       password: credentials.password);

   if (result.isSignedIn) {
     print('User signed in success');
   } else {
     print('User could not be signed in');
   }
 } on AuthException catch (authError) {
   print('Could not login - ${authError}');
 }
}

Amazon Cognito는 AWS STS와 동일한 자격 증명 제공자를 지원하며 인증되지 않은(게스트) 액세스도 지원하고 로그인하면 사용자 데이터를 마이그레이션 할 수 있습니다. Amazon Cognito는 디바이스를 바꿔가며 이용해도 데이터를 보존하도록 사용자 데이터 동기화를 위한 API 작업도 제공합니다.

4. 자격 증명 관리를 위한 기타 도구 활용하기

AWS Systems Manager 파라미터 스토어

AWS Systems Manager 는 AWS 계정 전체에서 구성 파라미터 및 암호를위한 안전한 중앙 집중식 스토리지를 제공하는 Parameter Store 라는 기능을 제공합니다. 구성 매개 변수, 자격 증명 및 라이센스 키와 같은 일반 텍스트 또는 암호화 된 데이터를 저장할 수 있습니다. 저장되면 세분화 된 액세스를 구성하여 애플리케이션에서 이러한 매개 변수를 얻을 수있는 사람을 지정하고 데이터를 보호하기위한 또 다른 보안 계층을 추가 할 수 있습니다. 다음은 자바를 이용하여 Amazon DynamoDB를 안전하게 사용하는 방법을 보여줍니다.

AWSSimpleSystemsManagement ssm = AWSSimpleSystemsManagementClientBuilder.defaultClient();
 
...
 
AmazonDynamoDB getDynamoDbClient() {
    //Getting AWS  credentials from Secrets Manager using GetParameter
    BasicAWSCredentials differentAWSCreds = new BasicAWSCredentials(
            getParameter("access-key"),
            getParameter("secret-key"));
 
    //Initialize the DDB Client with different credentials
    final AmazonDynamoDB client = AmazonDynamoDBClient.builder()
            .withCredentials(new AWSStaticCredentialsProvider(differentAWSCreds))
            .withRegion(getParameter("region")) //Getting config from Parameter Store
            .build();
    return client;
}
 
public GetParameterResult getParameter(String parameterName) {
    GetParameterRequest request = new GetParameterRequest();
    request.setName(parameterName);
    request.setWithDecryption(true);
    return ssm.newGetParameterCall().call(request).getParameter().getValue();
}

예를 들어, 데이터베이스 액세스 자격 증명 (사용자 이름 및 암호)을 파라미터 스토어에 저장하고 AWS Key Management Service 에서 관리하는 암호화 키로 암호화하고 애플리케이션을 실행하는 EC2 인스턴스에 해당 자격 증명을 읽고 해독 할 수있는 권한을 부여 할 수 있습니다. AWS Systems Manager Parameter Store 사용에 대한 자세한 내용은 이 링크를 참조하십시오.

AWS Secrets Manager

AWS Secrets Manager 는 순환, 감사 및 액세스 제어를 포함하여 조직에서 사용되는 암호의 수명주기를 중앙에서 관리 할 수있는 서비스입니다. 암호를 자동으로 교체 할 수 있도록함으로써 Secrets Manager는 보안 및 규정 준수 요구 사항을 충족하는 데 도움이 될 수 있습니다. Secrets Manager는 또한 Amazon RDS에서 MySQL, PostgreSQL 및 Amazon Aurora에 대한 기본 통합을 제공하며 다른 서비스와 함께 사용할 수 있습니다.

Git-secrets 활용하기

GitHub의 리포지토리를 사용하고 계시다면 git-secrets을 이용해서 Git 저장소에 AWS 자격 증명이 업로드되는 것을 방지할 수 있습니다. git-secrets은 커밋을 미리 스캔하여 자격 증명이 공개적으로 공유되는 것을 막아줍니다. git-secrets은 스캔 결과에 따라 커밋을 승인하거나 거부할 수 있습니다.

마무리

AWS 인증 자격 증명 뿐만 아니라 각종 관리자 아이디 및 암호, 데이터베이스 아이디나 암호 같은 모든 종류의 접근 키를 애플리케이션 설정 또는 소스에 하드 코딩하는 것은 좋은 방법이 아닙니다. 인증 정보를 하드 코딩하면 ID 및 키가 어떤 경우로도 노출될 위험이 있습니다.

이 글에서는 AWS 인증 자격 증명을 코드에 노출하지 않고 AWS IAM 역할과 STS를 사용하여 서비스간에 통합을 보다 쉽고 안전하게 구현하는 방법을 살펴 보았습니다. 또한, Amazon Cognito, AWS Systems Manager 파라미터 스토어, AWS Secrets Manager, Git-secrets 같은 AWS 서비스 및 도구를 활용하여 보안 자격 증명을 안전하게 저장, 검색 및 교체하는 방법을 소개하였습니다.

오늘 소개한 방법을 좀 더 자세히 알고 싶으시다면, AWS Builders DevSecOps 온라인 웨비나에 초대합니다. 핸즈온랩을 통한 보안 실습도 준비했으니 많은 도움이 되실 것입니다.

AWS에서 더욱 안전하게 애플리케이션을 만들어보세요.

– 정우철, AWS Solutions Architect