Обеспечение соблюдения стандартов шифрования для S3-объектов

Недавно мы провели внутреннюю дискуссию о том, как обеспечить шифрование объектов, загружаемых в S3, и было две основные теории о том, что нужно сделать, чтобы новые объекты были зашифрованы:

  1. Включить шифрование по умолчанию в ведре
  2. Включить шифрование по умолчанию в ведре и блокировать незашифрованные загрузки с помощью политики ведра.

В прошлом традиционно считалось, что для обеспечения шифрования объектов в S3 необходима политика bucket, которая явно запрещает вызовы PutObject, не устанавливающие соответствующие заголовки шифрования.
Некоторое время назад (мы не смогли найти объявление) S3 добавила возможность шифровать объекты в ведре по умолчанию.

Мы не были уверены, достаточно ли этой настройки для гарантии шифрования каждого нового объекта, потому что думали, что можно явно сказать «No encryption please!» при загрузке.
Чтобы решить этот вопрос, мы заглянули в документацию API-вызова PutObject и обнаружили следующее о заголовках шифрования:

По сути, нет способа явно сказать: «Я не хочу шифровать этот объект», потому что это уже является поведением API-вызова по умолчанию.
Это означает, что если вы включите шифрование по умолчанию в вашем ведре, объекты будут зашифрованы каким-либо образом.
Это не обязательно означает, что вы выбрали именно этот способ.
Если я установил для своего ведра шифрование по умолчанию с использованием KMS-ключа (SSE-KMS), я все равно могу использовать заголовок x-amz-server-side-encryption = AWS256 для изменения шифрования объекта на управляемое шифрование S3 (SSE-S3), что — в зависимости от ваших требований соответствия — может быть проблемой.

Давайте быстро вспомним различные виды шифрования на стороне сервера в S3, прежде чем говорить о том, как решить эту конкретную проблему:

  • SSE-S3 использует симметричное шифрование AES256 для шифрования ваших объектов, а S3 управляет ключами. Вы не получаете никакой видимости и никакого контроля над ключом — это, по сути, проверка соответствия стандарту «да, мои объекты зашифрованы в состоянии покоя», которая не дает практически никакой дополнительной безопасности в плане управления доступом.
  • SSE-KMS использует те же базовые алгоритмы шифрования, что и SSE-S3, но для создания ключей данных используется управляемый клиентом ключ в KMS, что означает, что вы получаете дополнительную видимость управления ключами и больший контроль над тем, кто может расшифровать ваши данные — это наш личный фаворит.
  • SSE-C позволяет вам указать ключ шифрования для шифрования каждого объекта, но от вас требуется управление ключом, т.е. S3 не будет хранить ваш ключ и только выполнять шифрование и расшифровку объектов. Вы, вероятно, захотите сделать это, только если вы действительно параноик или работаете в строго регламентированной среде. Это очень трудоемко и легко испортить.

После этого учебника мы можем поговорить о проблеме изменения шифрования.
Я сторонник того, чтобы клиенты не знали и не беспокоились о том, какое шифрование мы применяем в S3 для загрузки объектов, поэтому мы можем блокировать любой запрос с политикой ведра, в котором присутствуют заголовки шифрования.
Однако это будет наказывать клиентов, которые указывают правильный ключ шифрования, что мне также не нравится.
В идеале политика bucket запрещает только запросы с неправильной конфигурацией шифрования.
Давайте попробуем создать нечто подобное.

Поскольку для шифрования по умолчанию нельзя использовать SSE-C (S3 не может знать ключ, если его предоставляет пользователь), нам остается рассмотреть только SSE-S3 и SSE-KMS.

Начнем с самого простого случая: SSE-S3.
Когда мы хотим принудительно использовать опцию шифрования SSE-S3, нам нужно отклонять все запросы, имеющие набор заголовков x-amz-server-side-encryption = aws:kms.
Правило для этого может выглядеть следующим образом (Вам необходимо обновить $BucketName):

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Deny",
            "Principal": "*",
            "Action": "s3:PutObject",
            "Resource": "arn:aws:s3:::$BucketName/*",
            "Condition": {
                "Null": {
                    "s3:x-amz-server-side-encryption": "false"
                },
                "StringNotEqualsIfExists": {
                    "s3:x-amz-server-side-encryption": "AES256"
                }
            }
        },
        {
            "Sid": "AllowSSLRequestsOnly",
            "Action": "s3:*",
            "Effect": "Deny",
            "Resource": [
                "arn:aws:s3:::$BucketName",
                "arn:aws:s3:::$BucketName/*"
            ],
            "Condition": {
                "Bool": {
                    "aws:SecureTransport": "false"
                }
            },
            "Principal": "*"
        }
    ]
}
Войти в полноэкранный режим Выйти из полноэкранного режима

В случае с SSE-KMS все становится еще интереснее.
Здесь нам нужно отклонить все запросы, которые используют неправильный тип шифрования, т.е. x-amz-server-side-encryption = AWS256 или неправильный ключ KMS, т.е. значение x-amz-server-side-encryption-aws-kms-key-id.
Правило для этого выглядит следующим образом (Вам нужно заменить $BucketName, $Region, $Accountid и $KeyId):

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Deny",
            "Principal": "*",
            "Action": "s3:PutObject",
            "Resource": "arn:aws:s3:::$BucketName/*",
            "Condition": {
                "StringNotEqualsIfExists": {
                    "s3:x-amz-server-side-encryption": "aws:kms"
                },
                "Null": {
                    "s3:x-amz-server-side-encryption": "false"
                }
            }
        },
        {
            "Effect": "Deny",
            "Principal": "*",
            "Action": "s3:PutObject",
            "Resource": "arn:aws:s3:::$BucketName/*",
            "Condition": {
                "StringNotEqualsIfExists": {
                    "s3:x-amz-server-side-encryption-aws-kms-key-id": "arn:aws:kms:$Region:$AccountId:key/$KeyId"
                }
            }
        },
        {
            "Sid": "AllowSSLRequestsOnly",
            "Action": "s3:*",
            "Effect": "Deny",
            "Resource": [
                "arn:aws:s3:::$BucketName",
                "arn:aws:s3:::$BucketName/*"
            ],
            "Condition": {
                "Bool": {
                    "aws:SecureTransport": "false"
                }
            },
            "Principal": "*"
        }
    ]
}
Войдите в полноэкранный режим Выход из полноэкранного режима

Мы также создали небольшое приложение CDK, чтобы проверить, как оно работает.
По сути, оно устанавливает два ведра, одно из них с SSE-S3, а другое с SSE-KMS.
В первом ведре используется первая политика, о которой мы говорили выше, а во втором — вторая.
Есть также лямбда-функция с несколькими модульными тестами, написанными на python, которые проверяют семь различных сценариев, чтобы убедиться, что политики действительно работают так, как задумано.
Вы можете проверить все это в этом репозитории Github, если хотите попробовать сами.

def test_put_without_encryption_to_sse_s3_bucket_should_work(self):
    """Default encryption should take over when we specify nothing"""

def test_put_without_encryption_to_sse_kms_bucket_should_work(self):
    """Default encryption should take over when we specify nothing"""

def test_put_with_explicit_encryption_to_sse_s3_bucket_should_work(self):
    """Explicitly setting the correct encryption type should work"""

def test_put_with_explicit_encryption_to_sse_kms_bucket_should_work(self):
    """Explicitly setting the correct encryption type should work"""

def test_sse_kms_to_sse_s3_fails(self):
    """
    Assert that we get an error when we try to store SSE-KMS encrypted
    objects in the bucket that is SSE-S3 encrypted.
    """

def test_sse_s3_to_sse_kms_fails(self):
    """
    Assert that we get an error when we try to store SSE-S3 encrypted
    objects in the bucket that is SSE-KMS encrypted.
    """

def test_wrong_kms_key_fails(self):
    """
    Assert that a put request to the SSE-KMS encrypted bucket with a
    different KMS key fails.
    """
Вход в полноэкранный режим Выход из полноэкранного режима

Заключение

Если ваша цель — обеспечить шифрование ваших объектов в состоянии покоя в S3, шифрование по умолчанию в S3 — подходящий инструмент для этой работы.
Когда вам нужно обеспечить использование определенного метода шифрования, шифрование по умолчанию S3 в сочетании с политикой ведра поможет вам достичь этой цели.
В этом посте мы предоставили вам инструменты для достижения этой цели.

Отдельное спасибо Булелани и его коллегам из службы поддержки AWS, которые помогли нам с отладкой политик.

Мы надеемся, что вам понравилось читать эту статью.
Для обратной связи, вопросов и всего, чем вы хотите поделиться, не стесняйтесь обращаться к нам в Twitter (@Maurice_Brg & @Megaproaktiv

— Морис и Гернот


Этот пост был первоначально опубликован в сентябре 2020 года, но после недавнего обновления я подумал, что будет хорошей идеей поделиться им здесь.

Оцените статью
Procodings.ru
Добавить комментарий