암호화 키 관리(Encryption Key Management)
이 페이지의 내용
이 가이드에서는 CSFLE(클라이언트 사이드 필드 레벨 암호화 )가 활성화된 애플리케이션에서 KMS(키 관리 시스템)로 암호화 키를 관리하는 방법을 알아볼 수 있습니다.
암호화 구성 요소
MongoDB는 다음 구성 요소를 사용하여 클라이언트 측 필드 레벨 암호화를 수행합니다.
데이터 암호화 키(DEK)
고객 마스터 키(CMK)
키 볼트 컬렉션
키 관리 시스템(KMS)
키와 키 볼트에 대해 자세히 알아보려면 키와 키 볼트를 참조하세요.
지원되는 키 관리 서비스
클라이언트측 필드 레벨 암호화는 다음 키 관리 시스템 제공자를 지원합니다.
Amazon Web Services KMS
Azure Key Vault
Google Cloud KMS
KMIP를 준수하는 모든 키 관리 시스템
로컬 키 제공자(테스트 전용)
기본 KMIP 프로토콜 버전은 1.2입니다. MongoDB 서버 구성 파일에서 KMIP 버전 1.0 또는 1.1을 사용하도록 MongoDB를 구성할 수 있습니다
애플리케이션에서 이러한 제공자를 사용하여 클라이언트 사이드 필드 레벨 암호화를 수행하는 방법을 보여주는 다이어그램을 포함하여 이러한 제공자에 대해 자세히 알아보려면 CSFLE KMS 제공자를 참조하세요.
원격 키 관리 시스템을 사용하는 이유
원격 키 관리 시스템을 사용하여 고객 마스터 키를 관리하면 로컬 파일 시스템을 사용하여 CMK를 호스팅하는 것에 비해 다음과 같은 이점이 있습니다.
액세스 감사를 통한 안전한 키 보관
액세스 권한 문제 발생 위험 감소
원격 클라이언트에 대한 키의 가용성 및 배포
자동화된 키 백업 및 복구
중앙 집중식 암호화 키 수명 주기 관리
또한 다음과 같은 KMS 제공자의 경우, KMS가 데이터 암호화 키를 원격으로 암호화 및 복호화하여 고객 마스터 키가 CSFLE 지원 애플리케이션에 노출되지 않도록 합니다.
Amazon Web Services KMS
Azure Key Vault
Google Cloud KMS
데이터 암호화 키의 대체 이름 관리
데이터 암호화 키에 대체 이름을 할당하여 키를 더 쉽게 참조할 수 있습니다. 대체 이름을 지정하면 다음 작업을 수행할 수 있습니다.
_id
필드와 다른 방법으로 DEK를 참조합니다.런타임에 DEK를 동적으로 할당합니다.
대체 이름으로 데이터 암호화 키 생성하기
중요
전제 조건
새 키 대체 이름을 추가하기 전에 keyAltNames
필드에 고유 인덱스를 생성해야 합니다. 클라이언트 측 필드 레벨 암호화는 서버에서 적용되는 키 대체 이름의 고유성에 따라 달라집니다.
고유 인덱스를 만드는 방법을 알아보려면 고유 인덱스를 참조하세요.
다음 예에서는 대체 이름을 사용하여 데이터 암호화 키를 생성합니다. 드라이버 언어에 해당하는 탭을 선택합니다.
var autoEncryptionOpts = { keyVaultNamespace: keyVaultNamespace, kmsProviders: kmsProviders, }; var encryptedClient = Mongo( connectionString, autoEncryptionOpts ); var clientEncryption = encryptedClient.getClientEncryption(); var masterKey = { "<Your dataKeyOpts Key>": "<Your dataKeyOpts Value>", }; var keyVault = encryptedClient.getKeyVault(); var keyId = keyVault.createKey("aws", masterKey, ["<Your Key Alt Name>"]);
var keyVaultClient = new MongoClient(connectionString); var clientEncryptionOptions = new ClientEncryptionOptions( keyVaultClient: keyVaultClient, keyVaultNamespace: keyVaultNamespace, kmsProviders: kmsProviders); var clientEncryption = new ClientEncryption(clientEncryptionOptions); var dataKeyOptions = new DataKeyOptions( alternateKeyNames: new[] { "<Your Key Alt Name>" }, masterKey: new BsonDocument { { "<Your dataKeyOpts Keys>", "<Your dataKeyOpts Values>" }, }); var dataKeyId = clientEncryption.CreateDataKey("<Your KMS Provider>", dataKeyOptions, CancellationToken.None);
clientEncryptionOpts := options.ClientEncryption().SetKeyVaultNamespace(KeyVaultNamespace).SetKmsProviders(kmsProviders) keyVaultClient, err := mongo.Connect(context.TODO(), options.Client().ApplyURI(URI)) if err != nil { return fmt.Errorf("Client connect error %v", err) } clientEnc, err := mongo.NewClientEncryption(keyVaultClient, clientEncryptionOpts) if err != nil { return fmt.Errorf("NewClientEncryption error %v", err) } defer func() { _ = clientEnc.Close(context.TODO()) }() masterKey := map[string]interface{}{ "<Your dataKeyOpts Key>": "<Your dataKeyOpts Value>", } dataKeyOpts := options.DataKey(). SetMasterKey(masterKey). SetKeyAltNames([]string{"<Your Key Alt Name>"}) dataKeyID, err := clientEnc.CreateDataKey(context.TODO(), provider, dataKeyOpts) if err != nil { return fmt.Errorf("create data key error %v", err) }
ClientEncryptionSettings clientEncryptionSettings = ClientEncryptionSettings.builder() .keyVaultMongoClientSettings(MongoClientSettings.builder() .applyConnectionString(new ConnectionString(connectionString)) .build()) .keyVaultNamespace(keyVaultNamespace) .kmsProviders(kmsProviders) .build(); ClientEncryption clientEncryption = ClientEncryptions.create(clientEncryptionSettings); BsonDocument masterKeyProperties = new BsonDocument(); masterKeyProperties.put("provider", new BsonString("<Your KMS Provider>")); masterKeyProperties.put("<Your dataKeyOpts Key>", new BsonString("<Your dataKeyOpts Value>")); List keyAltNames = new ArrayList<String>(); keyAltNames.add("<Your Key Alt Name>"); BsonBinary dataKeyId = clientEncryption.createDataKey(kmsProvider, new DataKeyOptions().masterKey(masterKeyProperties).keyAltNames(keyAltNames));
const encryption = new ClientEncryption(client, { keyVaultNamespace, kmsProviders, }); const masterKey = { "<Your dataKeyOpts Key>": "<Your dataKeyOpts Value>", }; const key = await encryption.createDataKey(provider, { masterKey: masterKey, keyAltNames: ["<Your Key Alt Name>"], });
client = MongoClient(connection_string) client_encryption = ClientEncryption( kms_providers, key_vault_namespace, client, CodecOptions(uuid_representation=STANDARD), ) master_key={ "<Your dataKeyOpts Key>" : "<Your dataKeyOpts Value>"} data_key_id = client_encryption.create_data_key(provider, master_key, key_alt_names=["<Your Key Alt Name>"])
dataKeyOpts
및 kmsProviders
객체에 대해 자세히 알아보려면 CSFLE KMS 제공자를 참조하세요.
자동 암호화 스키마에서 키 대체 이름 사용
암호화 스키마에는 암호화해야 하는 필드와 해당 필드를 암호화하는 방법을 식별하는 사용자 지정 규칙이 포함되어 있습니다. 암호화 규칙에서 필드를 암호화하는 데이터 암호화 키에 대해한 대체 키 이름을 지정할 수 있습니다.
JSON 포인터를 사용하여 키 대체 이름을 참조해야 합니다. JSON 포인터는 "/"
문자가 접두사로 붙은 문자열로, 같은 문서 또는 다른 문서의 특정 필드 값에 액세스하는 데 사용할 수 있습니다. JSON 포인터를 사용하여 쿼리의 필드를 참조하거나 키 대체 이름의 값이 포함된 문서를 업데이트합니다.
중요
결정론적으로 암호화된 필드에는 대체 이름을 사용할 수 없음
결정론적 암호화 알고리즘으로 필드를 암호화할 때는 DEK의 대체 이름으로 DEK를 참조할 수 없습니다. 필드를 결정론적으로 암호화하려면 필드 암호화에 사용할 키의 _id
를 지정해야 합니다.
암호화 스키마의 참조 키 대체 이름
salary
필드를 암호화하는 다음 암호화 스키마를 가정해 보겠습니다.
{ "<database>.<collection>": { "bsonType": "object", "properties": { "salary": { "encrypt": { "bsonType": "int", "keyId": "/fieldWithAltName", "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random" } } } } }
스키마의 keyId
필드에는 암호화되는 문서 내의 fieldWithAltName
필드를 참조하기 위한 JSON 포인터가 포함되어 있습니다.
다음 문서의 fieldWithAltName
값은 my-alt-name
입니다.
{ "name": "Jon Doe", "salary": 45000, "fieldWithAltName": "my-alt-name" }
salary
필드는 대체 이름 my-alt-name
을 가진 DEK에 의해 암호화됩니다.
런타임에 동적으로 키 할당
대체 키 이름을 사용하여 런타임 시 필드에 대한 데이터 암호화 키를 동적으로 설정할 수 있습니다. 이 기능을 사용하면 동일한 암호화 스키마를 사용하여 서로 다른 DEK로 개별 문서를 암호화할 수 있습니다.
예를 들어 다음 문서를 가정해 보겠습니다.
{ "name": "Jon Doe", "salary": 45000, "fieldWithAltName": "my-alt-name" }, { "name": "Jane Smith", "salary": 70000, "fieldWithAltName": "my-other-alt-name" }
이전 예제의 암호화 스키마로 구성된 CSFLE 지원 클라이언트를 사용하여 앞의 문서를 삽입합니다.
암호화 스키마에서 salary.encrypt.keyId
필드에는 삽입된 문서의 fieldWithAltName
필드에 대한 JSON 포인터가 포함되어 있습니다. 결과적으로 두 예시 문서의 salary
필드는 각각 개별 문서에 특정한 DEK를 사용하여 암호화됩니다. 키는 런타임에 동적으로 할당됩니다.
절차: Mongo 셸을 사용하여 암호화 키 순환
Mongo 셸 버전 1.5 이상에서는 rewrapManyDataKey
메서드를 사용하여 암호화 키를 순환시킬 수 있습니다. rewrapManyDataKey
메서드는 여러 데이터 키를 자동으로 해독하고 지정된 고객 마스터 키를 사용하여 다시 암호화합니다. 그런 다음 키 볼트 컬렉션에서 순환된 키를 업데이트합니다. 이 메서드를 사용하면 두 개의 선택적 인수를 기반으로 암호화 키를 순환시킬 수 있습니다.
순환할 키를 지정하는 데 사용되는 필터입니다. 지정된 필터와 일치하는 데이터 키가 없으면 키가 순환되지 않습니다. 키 볼트 컬렉션의 모든 키를 순환시키려면 필터를 생략합니다.
새 고객 마스터 키(CMK)를 나타내는 객체입니다. 현재 CMK를 사용하여 데이터 키를 회전하려면 이 객체를 생략합니다.
rewrapManyDataKey
는 다음 구문을 사용합니다.
keyVault = db.getKeyVault() keyVault.rewrapManyDataKey( { "<Your custom filter>" }, { provider: "<KMS provider>", masterKey: { "<dataKeyOpts Key>" : "<dataKeyOpts Value>" } } )
KMS 제공자에 대한 dataKeyOpts
객체에 대해 자세히 알아보려면 지원되는 키 관리 서비스를 확인하세요.
데이터 암호화 키 삭제
표준 CRUD 삭제 작업을 사용하여 키 볼트 컬렉션에서 데이터 암호화 키를 삭제할 수 있습니다. DEK를 삭제하면 해당 DEK로 암호화된 모든 필드를 영구적으로 읽을 수 없게 됩니다.
팁
MongoDB Shell별 기능
MongoDB Shell에서는 다음과 같이 keyVault.deleteKey()
메서드를 사용하여 UUID
로 DEK를 삭제할 수 있습니다.
keyVault = db.getKeyVault() keyVault.deleteKey(UUID("<UUID String>"))
키 볼트 컬렉션에 대해 자세히 알아보려면 키 볼트 컬렉션을 참조하세요.
자세히 알아보기
지원되는 각 KMS 제공자에서 CSFLE 허용 애플리케이션을 설정하는 방법을 자세히 설명하는 튜토리얼은 다음 페이지를 참조하세요.
암호화 스키마의 추가 예를 보려면 CSFLE 암호화 스키마를 참조하세요.