S3 업로드가 서버 측 암호화와 호환되지 않음

기존 Discourse 서버를 AWS 기반 환경으로 마이그레이션하고 있으며, 업로드를 S3 버킷에 저장하는 과정에서 고객 관리 키를 사용한 서버 측 암호화(SSE-C)를 적용하고 있습니다. 복원 과정 중 업로드가 S3에 저장되지 않고 있으며, 모든 업로드가 실패하고 있습니다. tap|p 디버깅을 적절히 활용해 조사한 결과, 업로드 요청은 실제로 수행되고 있지만, 업로드 후 S3에서 반환된 etag가 매번 다르기 때문에 반환된 etag 검증이 실패하는 것으로 확인되었습니다. 예를 들어, 복원을 두 번 시도했을 때 the put_object call의 반환 값은 다음과 같습니다:

# Run 1
#<struct Aws::S3::Types::PutObjectOutput expiration=nil, etag="\"d49ec2006cfd6fe957af2f711edd9a4b\"", checksum_crc32=nil, checksum_crc32c=nil, checksum_sha1=nil, checksum_sha256=nil, server_side_encryption="aws:kms", version_id="xAF23wQ.zwpoxVmmGiTjxfX0svMZbHAe", sse_customer_algorithm=nil, sse_customer_key_md5=nil, ssekms_key_id="**redacted**", ssekms_encryption_context=nil, bucket_key_enabled=true, request_charged=nil>

# Run 2:
#<struct Aws::S3::Types::PutObjectOutput expiration=nil, etag="\"05edffee421c6aef950b3d4418ada293\"", checksum_crc32=nil, checksum_crc32c=nil, checksum_sha1=nil, checksum_sha256=nil, server_side_encryption="aws:kms", version_id="H2_8SVh.Yx2LKB4GIjhyPbVoj_.Vc1E2", sse_customer_algorithm=nil, sse_customer_key_md5=nil, ssekms_key_id="**redacted**", ssekms_encryption_context=nil, bucket_key_enabled=true, request_charged=nil>

(클라이언트 측 MD5 합과 파일명을 포함하는 put_object 요청 옵션도 함께 출력하고 있으므로, 이 두 파일이 동일하다는 것을 알고 있습니다.)

결국 ETag 응답 헤더는 SSE-C를 사용할 때… 다르게 동작합니다:

고객 제공 키(SSE-C) 또는 AWS Key Management Service(AWS KMS) 키(SSE-KMS)로 암호화된 객체의 ETag는 객체 데이터의 MD5 다이제스트가 아닙니다.

SSE-C 환경에서 업로드 무결성 검증을 수행할 수 있는 유일한 방법은 Content-MD5 요청 헤더를 보내고 S3가 손상 감지를 수행하도록 하는 것입니다. 또한 이미 업로드된 확인 로직도 SSE-C를 사용할 경우 깨지지만, 적어도 SKIP_ETAG_VERIFY를 사용하여 비활성화할 수는 있습니다.

즉시 PR을 제출하지 않는 이유는 이 문제를 해결하는 두 가지 다른 접근 방식이 있기 때문입니다:

  1. SKIP_ETAG_VERIFY를 업로드 후 검증까지 확장하는 것입니다. 이는 저렴하고 해킹적인 방식이며, 사용자가 SSE-C를 사용한다는 사실을 알고 해당 옵션을 활성화해야 한다는 점을 인지해야 합니다.
  2. 업로드 무결성 보호를 위해 Content-MD5 헤더(가능하면 항상)를 사용하는 방식으로 전환하는 것입니다. 이는 모든 사용자에게 작동한다는 장점이 있지만, 훨씬 더 큰 규모의 PR이 필요합니다.

(참고로, 이전에 아무도 이 문제에 부딪히지 않았다는 점이 다소 우려스럽습니다. 아무도 업로드에 SSE-C를 사용하여 Discourse를 사용하지 않는 걸까요?!?)

4개의 좋아요

제 추측은 "보안 업로드"가 매우 구체적인 경우에만 사용되기 때문에, 전체 업로드의 99.8%가 결국 공개적으로 서빙되는 것이며, 이로 인해 어떤 형태의 암호화-at-rest도 무용지물이 된다는 것입니다.

Matt, 여기서 틀린 말은 아니야. 내가 아는 한 이 주제가 처음 언급되는 거야.

(2)가 가능한 한 가장 적절한 접근 방식이라고 생각해.

그렇지 않으면 파일들이 모두 제대로 있는지에 대한 확신을 가질 수 없을 거야.

모든 업로드를 S3 버킷에 넣은 후, Discourse의 일반적인 운영은 문제없어? 일상적인 사용을 위해 다른 변경 사항이 필요한가 (인벤토리 같은 기능이 깨질 수 있다는 점을 고려하면, 사전 서명된 URL 등에 변경이 필요할 수도 있고…)?

5개의 좋아요

어떻게든 해보겠습니다. 다행히도 "etag"이 언급된 모든 위치를 찾으면 변경이 필요한 모든 코드 위치를 파악하는 데 충분할 것이며, 테스트 스위트가 잘 정리되어 있어 실행하고 변경해야 할 것을 비교적 쉽게 찾을 수 있습니다.

파괴될 수 있는 다른 것들에 대해서는, 아직 제대로 작동하는 사이트까지 도달하지 못했으며, 진행하면서 계속 보고/PR을 올리겠습니다. 인벤토리는 파괴되지 않아야 합니다. 왜냐하면 파일 이름이 아니라 파일 내용만 암호화되므로요. 저는 사전 서명된 URL을 사용하지 않기 때문에 그것이 파괴될지 여부는 아마도 알 수 없을 것입니다.

3개의 좋아요

방금 이 PR을 열었습니다. 이 PR은 S3 마이그레이션过程中的 ETag 기반 업로드 검증을 제거합니다. 알고 보니 이 기능은 S3로의 마이그레이션 중에만 사용되었고, 일반 업로드에서는 그냥 YOLO(무조건 시도) 방식으로 처리되고 있었네요. 덕분에 제 일이 훨씬 쉬워졌습니다. 이 PR과 이전에 제출한 ACL 비활성화 사이트 설정 PR 덕분에 이제 복원이 완료되었습니다. 다음 단계는 기능 테스트입니다.

6개의 좋아요

이제 병합된 것 같습니다. :partying_face:

1개의 좋아요

이 주제는 2일 후 자동으로 닫혔습니다. 더 이상 새 답변을 작성할 수 없습니다.