Ir al contenido
Entendiendo la rotación de claves de AWS KMS
Fotografía de Markus Winkler en Unsplash

Entendiendo la rotación de claves de AWS KMS

·2462 palabras·12 mins· loading · loading ·
AWS aws kms
Sergio Cambelo
Autor
Sergio Cambelo
Arquitecto Cloud
Tabla de contenido
Todo lo que siempre quiso saber sobre la rotación de claves de AWS KMS y nunca se atrevió a preguntar.

El otro día comentaba con un colega sobre las implicaciones de cambiar las claves KMS una vez que los datos ya están encriptados. El consenso fue que hay poca literatura al respecto. Por esa razón escribo este post para ofrecer una idea sobre cómo tratar con la rotación de claves en KMS.

Nota: Este post cubre un tema de nivel muy específico y avanzado. Se supone que el lector está familiarizado con el servicio AWS KMS y tiene experiencia práctica en su uso junto con otros servicios de AWS.

No hay duda de que cuando se trata de seguridad de datos en reposo, AWS Key Management Service (AWS KMS, o simplemente KMS para abreviar) es el mejor enfoque.

  • Funciona con la mayoría de los Servicios de AWS.

  • Es fácil de configurar.

  • Ofrece un buen equilibrio entre la simplicidad de la gestión y el control de claves.

En cuanto al equilibrio entre la facilidad de gestión y el control de claves, KMS ofrece tres tipos de claves.

  • AWS owned key: Claves propiedad y gestionadas por AWS. Son las más sencillas de usar pero no ofrecen control alguno al cliente. También estas claves se comparten entre los clientes.

  • AWS managed key: Claves propiedad del cliente pero gestionadas por AWS. Normalmente asociadas a un único servicio AWS. Estas claves viven solo en la cuenta de cliente AWS y no se comparten con otros clientes.

  • Customer managed key: Claves propiedad y gestionadas por el cliente, es decir, nosotros. Somos responsables del ciclo de vida de la clave, donde se utiliza y de su seguridad (configuración de la política de claves).

En este post voy a hablar de este último tipo de claves, las claves gestionadas por el cliente (CMKs).

Nota: Tras los cambios recientes en la documentación de AWS, voy a referirme a las CMK simplemente como claves KMS, en oposición a las claves AWS KMS utilizado para aquellas claves administradas por AWS.

¿De dónde vienen las llaves KMS?
#

Para entender la rotación de claves debemos saber cómo y de qué estan hechas las claves.

Para simplificar, estoy asumiendo que todas las claves que estamos usando en este post son de tipo *Symmetric y su tipo de uso es Encrypt y Decrypt.

Cuando creamos una clave KMS, elegimos el origen del material de cifrado.

  • KMS: Es el enfoque recomendado. AWS crea y gestiona el material de cifrado para la clave KMS.

  • Externo (Importar material clave): Usted crea e importa el material de cifrado para la clave KMS.

  • AWS CloudHSM key store: AWS KMS crea material de cifrado en el clúster AWS CloudHSM de su almacén de claves AWS CloudHSM.

  • Almacén de claves externo: El material de cifrado para la clave KMS se encuentra en un gestor de claves externo fuera de AWS.

A menos que tengamos una postura de seguridad muy estricta, KMS key material es la opción preferida para la mayoría de los casos. AWS CloudHSM key store y External key store son casos de uso avanzados que no están cubiertos por este post.

La principal diferencia entre el material de cifrado de KMS y el material de cifrado externo es que en el primero AWS es responsable de la creación, custodia y rotación del material de cifrado. Para este último, es nuestra responsabilidad exclusiva como clientes hacer todo eso. Un material de cifrado generado por KMS nunca sale del hardware donde se generó lo que hace muy difícil comprometerlo. Por otro lado, para un material de cifrado de cliente externo debemos encargarnos de asegurar el proceso de generación así como de almacenarlos de forma segura y duradera.

Conocer estas diferencias es importante para saber cómo rotar las claves KMS de manera eficiente.

Maneras de rotar una clave KMS
#

No hay una sola manera de rotar las claves KMS. Depende del tipo de material de cifrado utilizado y del control que nos gustaría tener en el proceso de rotación.

Rotación automática de claves
#

Para las claves KMS con material de cifrado gestionado por KMS es posible habilitar la rotación automática de claves una vez al año. Este proceso mantiene el mismo recurso lógico, nuestra clave KMS, pero rota su material de cifrado. Los materiales de cifrado antiguos de la clave se mantienen, y la única manera de eliminarlos es eliminar la clave KMS.

A partir de ese momento, las nuevas operaciones de cifrado utilizarán el nuevo material de cifrado de la clave, mientras que las operaciones de descifrado utilizarán el material de cifrado que se utilizó en el momento del cifrado. Esto garantiza que cualquier Data Key cifrada con esa clave KMS podrá ser descifrada.

Rotación manual
#

La rotación manual se puede utilizar en cualquiera de las claves con material de cifrado gestionado por KMS y claves con material de cifrado importado. También para las llaves con el material de cifrado importado es posible realizarlo de dos maneras diferentes:

Generar nueva clave
#

Para claves KMS con material de cifrado KMS y material de cifrado importado. Este método consiste en generar una nueva clave KMS y apuntar el alias KMS utilizado en la clave antigua a la nueva. De esta manera no es necesario realizar ningún cambio en nuestro código de aplicación o en los Servicios de AWS que hayan utilizado ese alias de KMS.

Cuando un alias de KMS apunta a otra clave de KMS, todas las nuevas operaciones de cifrado serán llevadas a cabo por la nueva clave de KMS, pero de una manera similar a lo que sucede con la rotación automática de claves, el descifrado de las claves de datos cifradas con la clave KMS antigua está garantizado siempre que las claves antiguas existan y estén habilitadas. La operación de descifrado utiliza metadatos para saber cuál es la clave de descifrado correspondiente.

Importar nuevo material clave
#

Este método solo es válido para claves KMS con material de cifrado importado. Consiste en cargar nuevo material de cifrado a la clave KMS. Sin embargo, a diferencia de la rotación automática de claves en claves KMS con material de cifrado KMS, para claves KMS con material de cifrado importado los materiales de cifrado antiguos no se guardan cuando se carga uno nuevo. Esto hace imposible descifrar cualquier clave de datos cifrada antes de la rotación del material de cifrado.

Qué hacer cuando una clave está comprometida
#

Aunque es muy difícil tener una clave KMS comprometida, no es imposible. Como vimos anteriormente, el material de cifrado administrado por AWS nunca sale del hardware donde se generó, pero para el material de cifrado administrado por el cliente es diferente.

Para las claves KMS con material de cifrado externo, el cliente debe encargarse de la custodia y seguridad del material de cifrado. Esto incluye almacenarlo de forma segura y no revelarlo como cualquier otro secreto o contraseña.

Veamos algunos ejemplos sobre cómo rotar una clave KMS dependiendo de dónde se usa. Estos ejemplos incluyen cómo restringir el acceso a claves con material de cifrado comprometido.

Rotar una clave KMS utilizada para generar data keys
#

Tenemos una clave KMS ee740549-6491-47b0-810d-1365b9b52792 con alias my-key para generar data keys que utilizaremos para cifrar nuestros archivos.

% aws kms list-aliases
{
    "Aliases": [
        {
            "AliasName": "alias/my-key",
            "AliasArn": "arn:aws:kms:us-east-1:372922107867:alias/my-key",
            "TargetKeyId": "ee740549-6491-47b0-810d-1365b9b52792",
            "CreationDate": "2024-01-23T15:06:33.424000+01:00",
            "LastUpdatedDate": "2024-01-23T15:06:33.424000+01:00"
        }
    ]
}
% aws kms generate-data-key --key-id alias/my-key --key-spec AES_256
{
    "CiphertextBlob": "AQIDAHgzqwxkDivbMS0RKdvlqyaQj/+MMUb4yxnnJYe+A6nwCQF8JkM9izf5rY6uVnY4n/uxAAAAfjB8BgkqhkiG9w0BBwagbzBtAgEAMGgGCSqGSIb3DQEHATAeBglghkgBZQMEAS4wEQQMUhGbd0AHxyS5TcwGAgEQgDvOECsCoq/uGWnt8N4QlS3lXvdKGuwUTRkdPoKaZuwbTQgkyd6rCZ/ez1xuobFGfnesf0yFnc34AnRYuw==",
    "Plaintext": "8RqgnZ3G+c5YzQTK3o9DnZAguHFbpWCoYC2aCNPg0lo=",
    "KeyId": "arn:aws:kms:us-east-1:372922107867:key/ee740549-6491-47b0-810d-1365b9b52792"
}

Podemos descifrar nuestro Ciphertext Blob usando la misma clave KMS.

% aws kms decrypt --ciphertext-blob AQIDAHgzqwxkDivbMS0RKdvlqyaQj/+MMUb4yxnnJYe+A6nwCQF8JkM9izf5rY6uVnY4n/uxAAAAfjB8BgkqhkiG9w0BBwagbzBtAgEAMGgGCSqGSIb3DQEHATAeBglghkgBZQMEAS4wEQQMUhGbd0AHxyS5TcwGAgEQgDvOECsCoq/uGWnt8N4QlS3lXvdKGuwUTRkdPoKaZuwbTQgkyd6rCZ/ez1xuobFGfnesf0yFnc34AnRYuw==
{
    "KeyId": "arn:aws:kms:us-east-1:372922107867:key/ee740549-6491-47b0-810d-1365b9b52792",
    "Plaintext": "8RqgnZ3G+c5YzQTK3o9DnZAguHFbpWCoYC2aCNPg0lo=",
    "EncryptionAlgorithm": "SYMMETRIC_DEFAULT"
}

Ya tenemos datos cifrados con nuestra data key cuando nos damos cuenta de que nuestra clave KMS (el material de cifrado) se ha visto comprometida.

Si simplemente rotamos la clave con otra, apuntando el alias a la nueva clave todas las nuevas data keys que generemos estarán seguras, pero las actuales y los datos protegidos con ellas estarán en riesgo.

Este escenario es aún peor si reemplazamos el material de cifrado. Perderemos el acceso a la data key y los datos protegidos bajo ella.

El enfoque correcto es el siguiente:

  1. Volver a cifrar las data keys generadas con la clave KMS comprometida. Este proceso no altera el contenido de la data key, solo su cifrado, no siendo necesario volver a cifrar los datos que protege. Para esto usaremos una nueva clave KMS 9772c95e-c9c5-43fb-bfcd-f8c4678f9e49 y apuntaremos el alias my-key a ella.

    % aws kms update-alias --alias-name alias/my-key --target-key-id 9772c95e-c9c5-43fb-bfcd-f8c4678f9e49
    % aws kms list-aliases
    {
        "Aliases": [
            {
                "AliasName": "alias/my-key",
                "AliasArn": "arn:aws:kms:us-east-1:372922107867:alias/my-key",
                "TargetKeyId": "9772c95e-c9c5-43fb-bfcd-f8c4678f9e49",
                "CreationDate": "2024-01-23T15:06:33.424000+01:00",
                "LastUpdatedDate": "2024-01-23T16:54:02.384000+01:00"
            }
        ]
    }
    % aws kms re-encrypt --ciphertext-blob AQIDAHgzqwxkDivbMS0RKdvlqyaQj/+MMUb4yxnnJYe+A6nwCQF8JkM9izf5rY6uVnY4n/uxAAAAfjB8BgkqhkiG9w0BBwagbzBtAgEAMGgGCSqGSIb3DQEHATAeBglghkgBZQMEAS4wEQQMUhGbd0AHxyS5TcwGAgEQgDvOECsCoq/uGWnt8N4QlS3lXvdKGuwUTRkdPoKaZuwbTQgkyd6rCZ/ez1xuobFGfnesf0yFnc34AnRYuw== --destination-key-id alias/my-key
    {
        "CiphertextBlob": "AQICAHjupf6EVUcdZoJA0fyIbMmGwu8KRy7wa/C4PTQSmct1SgGGvw309CV/AUGYhIa3SWSdAAAAfjB8BgkqhkiG9w0BBwagbzBtAgEAMGgGCSqGSIb3DQEHATAeBglghkgBZQMEAS4wEQQMDsd/aY/QTctxYJ9uAgEQgDvZIU5u53bQyGHNwt1pKW+ylH6J4KIyGKV1whOA7SCkUBY3eaQgQbXWOSBgYe8Z5Fy/8gOBSl6SElwBUg==",
        "SourceKeyId": "arn:aws:kms:us-east-1:372922107867:key/ee740549-6491-47b0-810d-1365b9b52792",
        "KeyId": "arn:aws:kms:us-east-1:372922107867:key/9772c95e-c9c5-43fb-bfcd-f8c4678f9e49",
        "SourceEncryptionAlgorithm": "SYMMETRIC_DEFAULT",
        "DestinationEncryptionAlgorithm": "SYMMETRIC_DEFAULT"
    }
    
  2. Desactivar la clave KMS comprometida para evitar cualquier operación de descifrado.

    % aws kms disable-key --key-id ee740549-6491-47b0-810d-1365b9b52792
    
  3. A partir de ese momento solo será posible descifrar nuestra data key con la nueva clave KMS.

    % aws kms decrypt --ciphertext-blob AQIDAHgzqwxkDivbMS0RKdvlqyaQj/+MMUb4yxnnJYe+A6nwCQF8JkM9izf5rY6uVnY4n/uxAAAAfjB8BgkqhkiG9w0BBwagbzBtAgEAMGgGCSqGSIb3DQEHATAeBglghkgBZQMEAS4wEQQMUhGbd0AHxyS5TcwGAgEQgDvOECsCoq/uGWnt8N4QlS3lXvdKGuwUTRkdPoKaZuwbTQgkyd6rCZ/ez1xuobFGfnesf0yFnc34AnRYuw== 
    
    An error occurred (DisabledException) when calling the Decrypt operation: arn:aws:kms:us-east-1:372922107867:key/ee740549-6491-47b0-810d-1365b9b52792 is disabled.
    
    % aws kms decrypt --ciphertext-blob AQICAHjupf6EVUcdZoJA0fyIbMmGwu8KRy7wa/C4PTQSmct1SgGGvw309CV/AUGYhIa3SWSdAAAAfjB8BgkqhkiG9w0BBwagbzBtAgEAMGgGCSqGSIb3DQEHATAeBglghkgBZQMEAS4wEQQMDsd/aY/QTctxYJ9uAgEQgDvZIU5u53bQyGHNwt1pKW+ylH6J4KIyGKV1whOA7SCkUBY3eaQgQbXWOSBgYe8Z5Fy/8gOBSl6SElwBUg==
    {
        "KeyId": "arn:aws:kms:us-east-1:372922107867:key/9772c95e-c9c5-43fb-bfcd-f8c4678f9e49",
        "Plaintext": "8RqgnZ3G+c5YzQTK3o9DnZAguHFbpWCoYC2aCNPg0lo=",
        "EncryptionAlgorithm": "SYMMETRIC_DEFAULT"
    }
    

Rotar una clave KMS utilizada en S3
#

En este escenario tenemos un bucket S3 my-bucket-102353703712 con clave de cifrado predeterminada SSE-KMS 93237725-c352-41c7-a762-3f001e97c9af.

% aws s3api get-bucket-encryption --bucket my-bucket-102353703712
{
    "ServerSideEncryptionConfiguration": {
        "Rules": [
            {
                "ApplyServerSideEncryptionByDefault": {
                    "SSEAlgorithm": "aws:kms",
                    "KMSMasterKeyID": "arn:aws:kms:us-east-1:102353703712:key/93237725-c352-41c7-a762-3f001e97c9af"
                },
                "BucketKeyEnabled": false
            }
        ]
    }
}

Cualquier archivo que se añade al bucket se cifra automáticamente con esta clave KMS predeterminada.

% cat my-file.txt
File content

% aws s3 cp my-file.txt s3://my-bucket-102353703712
upload: ./my-file.txt to s3://my-bucket-102353703712/my-file.txt

Podemos comprobar que nuestro archivo está cifrado y la clave utilizada para ese propósito.

% aws s3api get-object --bucket my-bucket-102353703712 --key my-file.txt my-file.txt
{
    "AcceptRanges": "bytes",
    "LastModified": "2024-01-26T15:48:09+00:00",
    "ContentLength": 13,
    "ETag": "\"9c2d5a5c8d2d27ca8308ad71310ade58\"",
    "ContentType": "text/plain",
    "ServerSideEncryption": "aws:kms",
    "Metadata": {},
    "SSEKMSKeyId": "arn:aws:kms:us-east-1:102353703712:key/93237725-c352-41c7-a762-3f001e97c9af"
}
% cat my-file.txt
File content

En este punto, necesitamos rotar nuestra clave KMS al igual que en el escenario anterior. Para buckets S3 el mejor enfoque es volver a cifrar nuestros objetos del bucket una vez que cambiemos la clave KMS por defecto del bucket.

  1. Cambiar la clave KMS predeterminada del bucket. Este proceso no altera el cifrado de los objetos que ya existen en el bucket. Dado que estamos rotando nuestra clave KMS debido al hecho de que la clave ha sido comprometida, debemos tomar más medidas.

    % aws s3api put-bucket-encryption \
        --bucket my-bucket-102353703712 \
        --server-side-encryption-configuration '{"Rules": [{"ApplyServerSideEncryptionByDefault": {"SSEAlgorithm": "aws:kms","KMSMasterKeyID": "1e51777f-fefd-4bc5-8671-c1851e7d07b3"}}]}'
    
    % aws s3api get-bucket-encryption --bucket my-bucket-102353703712
    {
        "ServerSideEncryptionConfiguration": {
            "Rules": [
                {
                    "ApplyServerSideEncryptionByDefault": {
                        "SSEAlgorithm": "aws:kms",
                        "KMSMasterKeyID": "1e51777f-fefd-4bc5-8671-c1851e7d07b3"
                    },
                    "BucketKeyEnabled": false
                }
            ]
        }
    }
    
  2. Volver a cifrar los objetos del bucket. Para ello, simplemente podemos realizar una operación de copia para todo el bucket.

    Vale la pena mencionar que hay muchas maneras de lograr el re-cifrado de todos los objetos en un bucket. El presentado aquí es uno de los más simples, pero podría tener algunas limitaciones. Para buckets con miles o millones de objetos, podría ser aconsejable utilizar S3 Batch Operations.

    % aws s3 cp --recursive s3://my-bucket-102353703712 s3://my-bucket-102353703712
    copy: s3://my-bucket-102353703712/my-file.txt to s3://my-bucket-102353703712/my-file.txt
    
    % aws s3api get-object --bucket my-bucket-102353703712 --key my-file.txt my-file.txt
    {
        "AcceptRanges": "bytes",
        "LastModified": "2024-01-26T16:13:22+00:00",
        "ContentLength": 13,
        "ETag": "\"6b856ce783e96cac811638a2a071fc22\"",
        "ContentType": "text/plain",
        "ServerSideEncryption": "aws:kms",
        "Metadata": {},
        "SSEKMSKeyId": "arn:aws:kms:us-east-1:102353703712:key/1e51777f-fefd-4bc5-8671-c1851e7d07b3"
    }
    % cat my-file.txt
    File content
    
  3. Por último, deshabilitamos la clave KMS comprometida para evitar su uso.

    % aws kms disable-key --key-id 93237725-c352-41c7-a762-3f001e97c9af
    

Rotar una clave KMS utilizada en volúmenes EBS
#

Dado que los volúmenes EBS no pueden volver a cifrarse, la rotación de las claves KMS debe abordarse indirectamente. La única forma de volver a cifrar el contenido de un volumen EBS es generar una instantánea y crear un nuevo volumen a partir de ella utilizando una clave KMS diferente.

Nuestro punto de partida es un volumen EBS cifrado con una clave KMS d6f8b1ee-032c-4244-9d44-827861e6f9fa.

% aws ec2 describe-volumes --volume-id vol-09be7ec8867c85f21
{
    "Volumes": [
        {
            "Attachments": [],
            "AvailabilityZone": "us-east-1a",
            "CreateTime": "2024-01-27T17:56:36.159000+00:00",
            "Encrypted": true,
            "KmsKeyId": "arn:aws:kms:us-east-1:386088430154:key/d6f8b1ee-032c-4244-9d44-827861e6f9fa",
            "Size": 10,
            "SnapshotId": "",
            "State": "available",
            "VolumeId": "vol-09be7ec8867c85f21",
            "Iops": 3000,
            "VolumeType": "gp3",
            "MultiAttachEnabled": false,
            "Throughput": 125
        }
    ]
}

Como en ejemplos anteriores, debemos rotar la clave KMS. Procedemos de la siguiente manera:

  1. Crear una instantánea del volumen.

    % aws ec2 create-snapshot --volume-id vol-09be7ec8867c85f21
    {
        "Description": "",
        "Encrypted": true,
        "OwnerId": "386088430154",
        "Progress": "",
        "SnapshotId": "snap-0a908c9b806fbadc5",
        "StartTime": "2024-01-27T18:06:13.864000+00:00",
        "State": "pending",
        "VolumeId": "vol-09be7ec8867c85f21",
        "VolumeSize": 10,
        "Tags": []
    }
    
  2. Entonces, crear un nuevo volumen a partir de la instantánea que hace referencia a la nueva clave KMS.

    % aws ec2 create-volume --availability-zone us-east-1a --encrypted --kms-key-id 3d70f1b8-7a3b-4e9e-a6b9-6db1a6471f5d --snapshot-id snap-0a908c9b806fbadc5 --volume-type gp3 
    {
        "AvailabilityZone": "us-east-1a",
        "CreateTime": "2024-01-27T18:15:47+00:00",
        "Encrypted": true,
        "KmsKeyId": "3d70f1b8-7a3b-4e9e-a6b9-6db1a6471f5d",
        "Size": 10,
        "SnapshotId": "snap-0a908c9b806fbadc5",
        "State": "creating",
        "VolumeId": "vol-017bbdf1b17c1c8b6",
        "Iops": 3000,
        "Tags": [],
        "VolumeType": "gp3",
        "MultiAttachEnabled": false,
        "Throughput": 125
    }
    
  3. Por último, desactivar la antigua calve KMS y eliminar el volumen original y su instantánea.

    % aws kms disable-key --key-id d6f8b1ee-032c-4244-9d44-827861e6f9fa
    
    % aws ec2 delete-volume --volume-id vol-09be7ec8867c85f21
    
    % aws ec2 delete-snapshot --snapshot-id snap-0a908c9b806fbadc5
    

Rotar una clave KMS utilizada en otros servicios de almacenamiento.
#

La rotación de las claves KMS en otros servicios de AWS varía de un servicio a otro, pero de una forma u otra podría extrapolarse de los ejemplos que se dan en este post. Por ejemplo, el proceso para rotar o cambiar la clave KMS para un clúster Aurora RDS consiste en tomar una instantánea del clúster y restaurarla mientras cambia su clave KMS de una manera similar a como lo hicimos con el volumen EBS.

Otros servicios sin embargo tienen un proceso de rotación más simple, por ejemplo en DynamoDB es tan sencillo y transparente como especificar una nueva clave KMS en las opciones de cifrado para la tabla y el servicio se encarga del proceso de re-cifrado.

Para finalizar
#

As we saw in this post, the chances of getting a KMS key compromised are very low since the AWS generated key material never leaves AWS hardware unencrypted. Customer generated key material should be used only for very specific use cases and the customer is responsible for the custody and durability of the key material.

Como hemos visto en este post, las posibilidades de comprometer una clave KMS son muy bajas ya que el material de cifrado generado por AWS nunca deja el hardware de AWS sin cifrar. El material de cifrado generado por el cliente debe usarse solo para casos de uso muy específicos y el cliente es responsable de la custodia y durabilidad del material de cifrado.

En conclusión, es posible rotar y bloquear las claves viejas, pero esto tiene una sobrecarga operativa. El procedimiento para la rotación de claves KMS varía de un servicio a otro y debe planificarse y probarse cuidadosamente.


Referencias
#

Relacionados

Cómo resolver los problemas de rutas en los SPAs en AWS con CloudFront Functions
·1455 palabras·7 mins· loading · loading
AWS aws spa cloudfront
Amazon Aurora RDS :  Readers Auto Scaling y Custom Endpoints
·2078 palabras·10 mins· loading · loading
AWS aws rds autoscaling