암호화 키 사용

개요

기본적으로 Cloud Storage는 Google 관리 암호화 키와 AES256 암호화 알고리즘을 사용하여 모든 객체 데이터를 암호화합니다. 하지만 개발자가 제공하는 암호화 키 유형(고객 제공 암호화 키(CSEK)) 또는 개발자가 Google Cloud KMS을 통해 관리하는 암호화 키 유형(고객 관리 암호화 키(CMEK)) 중 하나를 사용할 수도 있습니다. Cloud Storage는 CSEK를 Google 서버에 영구 저장하거나 그 밖의 방법으로 관리하지 않습니다. 이러한 암호화 옵션에 대한 자세한 내용은 CMEKCSEK를 참조하세요.

gsutil은 JSON API를 사용하여 Cloud Storage 객체와 상호작용하는 데 CSEK를 허용합니다. 이 키는 다음과 같이 .boto 구성 파일을 통해 제공됩니다.

[GSUtil]
encryption_key = ...
decryption_key1 = ...
decryption_key2 = ...

각 키는 AES256 암호화 알고리즘과 함께 사용할 수 있도록 RFC 4648 Base64로 인코딩된 256비트 문자열 데이터입니다.

또한 gsutil은 JSON API를 사용하여 객체를 암호화하는 데 CMEK를 허용합니다. 일부 버킷에 새로 쓰는 모든 객체를 암호화하는 데 CMEK를 사용하려고 하는 경우에는 해당 버킷의 기본 KMS 키를 설정해야 합니다(gsutil help kms 참조). 또는 .boto 구성 파일에서 원하는 CMEK를 지정할 수 있지만 encryption_key 속성만 지정하면 됩니다.

[GSUtil]
encryption_key = projects/PROJECT_ID/locations/LOCATION/keyRings/KEYRING/cryptoKeys/KEYNAME

CSEK 암호화 객체를 복호화하려면 decrypt_key 속성 중 하나에 CSEK를 제공해야 하지만, 객체를 암호화하는 데 사용되는 CMEK의 이름은 객체 메타데이터에 저장되므로 CMEK 암호화 객체를 복호화하는 데 이렇게 할 필요가 없습니다.

boto 파일을 수정하지 않고 명령어별로 CMEK를 지정하려면 키 이름을 최상위 boto 옵션으로 지정하면 됩니다.

gsutil -o 'GSUtil:encryption_key=projects/PROJECT_ID/locations/LOCATION/keyRings/KEYRING/cryptoKeys/KEYNAME' \
       cp /some/local/file gs://my-bucket/

암호화 동작

.boto 구성 파일에는 encryption_key 하나와 decryption_key 여러 개를 지정할 수 있습니다.

encryption_key가 .boto 구성 파일에 있으면 gsutil은 Cloud Storage에서 쓰거나 복사하는 데이터가 이 키로 암호화되도록 합니다. encryption_key가 제공되지 않으면 gsutil은 쓰거나 복사하는 모든 데이터가 대상 버킷의 기본 암호화 유형을 대신 사용하도록 합니다. 버킷에 기본 KMS 키가 설정되어 있으면 암호화에 CMEK가 사용됩니다. 그렇지 않으면 Google 관리 암호화가 사용됩니다.

CSEK로 암호화된 객체에는 gsutil cat, cp, mv 또는 rsync 명령어를 통해 이러한 객체를 다운로드하거나 복사할 때 항상 일치하는 복호화 키가 필요합니다. 이러한 객체의 CRC32C 또는 MD5 해시를 ls -L 또는 stat 명령어를 사용하여 보려는 경우에도 일치하는 복호화 키가 필요합니다.

일치하는 키가 .boto 구성에 있으면 gsutil은 필요에 따라 Cloud Storage에 요청을 수행하고 복호화된 결과에 작업을 수행합니다. gsutil은 암호화된 데이터를 절대 로컬 디스크에 저장하지 않습니다.

gsutil은 키의 SHA256 해시를 CSEK의 해시와 비교하여 클라우드 객체에 사용할 올바른 CSEK를 자동으로 감지합니다. 또한 gsutil은 구성된 암호화 키와 복호화 키(최대 100개)를 고려하여 일치하는 항목을 검색합니다. 1부터 시작하여 오름차순으로 boto 구성 파일에 복호화 키를 나열해야 합니다. 다음 구성을 예시로 살펴보겠습니다.

decryption_key1 = ...
decryption_key9 = ...
decryption_key10 = ...
decryption_key11 = ...

decryption_key 2~8의 값이 제공되지 않으므로 decrypt_key 9, 10, 11은 무시됩니다.

재개 가능한 작업 및 암호화 키

쓰기 또는 복사 작업이 부분적으로 완료된 상태에서 boto 구성 파일의 encryption_key가 변경되면(예: ^C 키를 누른 후 gsutil cp 객체 업로드를 다시 실행한 경우 또는 네트워크 제한 시간이 발생한 경우) gsutil은 부분적으로 완료된 작업을 다시 시작하여 대상 객체를 새 키로 쓰도록 합니다.

고객 제공 암호화 키 생성

Python에서는 암호화 키로 사용할 256비트 RFC 4648 Base64로 인코딩된 문자열을 간편하게 생성할 수 있습니다.

python -c 'import base64; import os;\
           print(base64.encodestring(os.urandom(32)))'

고객 제공 암호화 키 관리

Google은 CSEK를 저장하지 않으므로 CSEK를 분실하면 이 키로 암호화된 모든 데이터에 대한 액세스 권한을 영구 손실하게 됩니다. 따라서 각 암호화 키를 안전한 위치에 백업하는 것이 좋습니다. 절대로 키를 .boto 구성 파일에만 저장해서는 안 됩니다.

또한 CSEK를 만들면 이 키와 객체에 대한 액세스 권한을 가지고 있는 사용자 누구나 객체의 데이터를 읽을 수 있습니다. 따라서 신뢰할 수 없는 사용자와 암호화 키를 공유하지 않도록 주의해야 합니다.

키 순환

CSEK를 순환하려면 encryption_key 구성 값을 decryption_key 구성 값으로 변경한 후 새로운 encryption_key 값을 사용하면 됩니다. 그러면 데이터를 다운로드한 후 다시 업로드할 필요 없이 다시 쓰기 명령어를 사용하여 클라우드에서 키를 순환할 수 있습니다. 예를 들어 초기 구성이 다음과 같다고 가정합니다.

# Old encryption key
encryption_key = keyA...

구성을 아래와 같이 변경할 수 있습니다.

# New encryption key
encryption_key = keyB...
# Encryption key prior to rotation
decryption_key1 = keyA...

그런 다음 다음을 실행하여 객체의 암호화 키를 순환할 수 있습니다.

gsutil rewrite -k gs://bucket/object temp-file

이 작업을 통해 다른 CMEK를 적용할 수도 있지만 decryption_keynoun 구성 값에 CMEK를 추가할 필요는 없습니다. 마찬가지로 encryption_key 구성 값에 지정된 키 유형에 따라 CSEK 암호화 또는 CMEK 암호화로 전환할 수 있습니다.

암호화 키가 성능에 미치는 영향

객체 나열을 수행할 때 CSEK 또는 CMEK로 암호화된 객체의 메타데이터에는 객체의 CRC32C 또는 MD5 해시가 포함되어 있지 않습니다. 이러한 필드가 필요한 명령어의 경우(예: gsutil ls -L) gsutil은 CSEK 또는 CMEK로 암호화된 각 객체에 메타데이터 GET 요청을 추가로 수행합니다. 따라서 -L 플래그를 사용하여 이러한 객체를 나열하려면 객체마다 추가 작업 하나 필요합니다. 이로 인해 Google 소유 키로 암호화된 객체를 나열할 때보다 속도가 훨씬 느려집니다.

고객 제공 암호화 키가 보안에 미치는 영향

gsutil은 항상 HTTPS를 통해 암호화 키를 전송하므로 CSEK는 네트워크상에서 결코 보이지 않습니다. 하지만 이 키는 gsutil을 실행하는 머신의 메모리뿐만 아니라 .boto 구성 파일에도 있습니다. 따라서 이 파일이나 머신이 손상된 경우 암호화 키도 손상된 것으로 간주해야 하며, 손상된 키로 암호화된 모든 객체에 즉시 키 순환을 수행해야 합니다.

Xml API 미지원

gsutil은 XML API를 사용하여 암호화된 객체와 상호작용할 수 없으며 encryption_key 또는 decryption_keys가 구성에 지정된 경우에 JSON API를 사용합니다.