Al final de 04-01 vam deixar una promesa a mig complir. La política pol-mercadofresco-tienda exigeix que cada miniatura es pugi amb s3:x-amz-server-side-encryption: aws:kms, i rol-lambda-miniaturas té concedits kms:Decrypt i kms:GenerateDataKey sobre una clau identificada per un UUID. Però aquella clau, alias/mercadofresco-datos, continua sent poc més que un nom: ningú no ha decidit qui l'administra, qui la pot fer servir, cada quant rota, ni què passa exactament quan S3 «xifra» un objecte.

I hi ha una raó de negoci al darrere. MercadoFresco guarda noms, adreces de repartiment, telèfons i historial de comandes de clients espanyols. Això són dades personals en el sentit del RGPD, i l'article 32 del reglament cita el xifratge explícitament com a mesura tècnica apropiada. Si un portàtil amb una còpia de la base de dades acaba al seient del darrere d'un taxi, la diferència entre «incident» i «bretxa notificable en 72 hores» sol ser si el contingut estava xifrat.

AWS Key Management Service (KMS) és el servei que custodia les claus de xifratge, controla qui les pot fer servir i registra cada ús. En aquesta lliçó la Marta l'entén de debò —inclòs el mecanisme del xifratge de sobre, que explica tota la resta— i xifra per fi les còpies de la base de dades, els volums i la instància mercadofresco-pedidos.

Advertiment. Els exemples d'aquesta lliçó són didàctics. Tota configuració de xifratge, gestió de claus o compliment normatiu (RGPD, PCI DSS, ENS) que s'hagi d'aplicar sobre dades reals de clients ha de ser revisada per un professional de seguretat o de compliment abans d'arribar a producció. El xifratge mal implementat dona una falsa sensació de seguretat, i una clau mal esborrada destrueix dades de manera irreversible. Tots els identificadors i dades d'aquest curs són ficticis.

Contingut

  1. Per què xifrar: el RGPD i les dades de MercadoFresco
  2. En repòs i en trànsit
  3. Criptografia simètrica i asimètrica, el just
  4. Què és KMS i què no és
  5. Tipus de clau
  6. Claus multiregió, material importat i CloudHSM
  7. El xifratge de sobre
  8. El desxifratge, pas a pas
  9. Xifratge del costat del client i del costat del servidor
  10. SSE-S3, SSE-KMS i SSE-C
  11. Les claus de bucket d'S3 i el cost de les peticions
  12. La política de clau i la seva relació amb IAM
  13. kms:ViaService i el context de xifratge
  14. Concessions (grants)
  15. Rotació automàtica i què significa realment
  16. Xifrar mercadofresco-copias-basedatos
  17. Xifrar volums d'EBS i snapshots
  18. Xifrar mercadofresco-pedidos, que ja existeix sense xifrar
  19. Compartir un snapshot xifrat amb un altre compte
  20. KMS des de boto3
  21. Límits, quotes i cost real per a MercadoFresco
  22. ACM no és KMS
  23. Eliminar una clau: els 7 a 30 dies d'espera

Per què xifrar: el RGPD i les dades de MercadoFresco

El xifratge no protegeix de tot. Protegeix d'un conjunt molt concret d'amenaces, i convé ser honest sobre quin:

Amenaça Ajuda el xifratge en repòs?
Algú roba un disc físic del centre de dades , totalment
Un snapshot o una còpia es comparteix per error amb un altre compte , si no té la clau
Un bucket queda exposat públicament amb SSE-KMS: a més del permís d'S3 cal el de KMS
Es compleix el requisit normatiu d'una auditoria , i a més amb registre de cada ús
Un atacant roba les credencials de l'aplicació No: l'aplicació té permís per desxifrar
Una injecció SQL extreu la taula de clients No: la base de dades retorna les dades ja en clar

Aquella taula és important: el xifratge en repòs és una capa, no un escut màgic. Les dues últimes files són exactament les raons per les quals 04-01 existeix i per les quals 04-05 existirà.

Per a MercadoFresco hi ha tres conjunts de dades que s'han de xifrar sense discussió:

  1. mercadofresco-copias-basedatos: còpies completes de la base de dades pedidos, amb noms, adreces i telèfons de tots els clients. És l'actiu més perillós del compte.
  2. La instància mercadofresco-pedidos i els seus snapshots automàtics.
  3. Els volums d'EBS de les instàncies de la botiga i el volum mercadofresco-fotos-01.

El catàleg de fotos públiques (productos/) no conté dades personals, però es xifra igualment perquè el cost marginal és zero i no haver de pensar en què està xifrat i què no és en si mateix un avantatge operatiu.

En repòs i en trànsit

Són dos problemes diferents, resolts per mecanismes diferents:

En repòs En trànsit
Què protegeix Dades desades al disc Dades viatjant per la xarxa
Mecanisme AES-256 sobre el volum, l'objecte o la fila TLS 1.2/1.3
Servei KMS ACM per als certificats
A MercadoFresco S3, EBS, RDS, snapshots HTTPS a CloudFront i a l'ALB (03-03, 03-05)
Com es força Xifratge per defecte del bucket, kms: a la política Deny amb aws:SecureTransport: false

El xifratge en trànsit ja està resolt des del mòdul 3: el certificat d'ACM a l'ALB i a CloudFront, i el Deny que vam escriure a pol-mercadofresco-tienda quan aws:SecureTransport és fals. Aquesta lliçó va de l'altre.

Criptografia simètrica i asimètrica, el just

No cal saber criptografia per fer servir KMS, però sí distingir dues famílies:

Simètrica. La mateixa clau xifra i desxifra. És rapidíssima: AES-256 xifra centenars de megabytes per segon en qualsevol CPU moderna. El seu problema és la distribució: cal fer arribar la clau a l'altre extrem sense que ningú la intercepti.

Asimètrica. Dues claus relacionades matemàticament: la pública xifra i la privada desxifra (o al revés, per signar). Resol la distribució —la pública es pot publicar— però és ordres de magnitud més lenta i només pot xifrar dades molt petites (una clau RSA de 2048 bits xifra com a molt 190 bytes).

Simètrica Asimètrica
Claus Una Parell pública/privada
Velocitat Molt ràpida Lenta
Mida de dades Il·limitada Centenars de bytes
Algorisme típic AES-256-GCM RSA-2048, ECC
Ús a KMS Per defecte, xifratge de dades Signatura, xifratge entre parts sense secret compartit

A KMS, si no demanes una altra cosa, totes les claus són simètriques AES-256. I la combinació de les dues idees —fer servir simètrica per a les dades i protegir aquella clau amb una altra clau— és exactament el xifratge de sobre que veiem d'aquí a dos apartats.

Què és KMS i què no és

KMS és un servei que:

  • Genera i custodia claus mestres dins de mòduls de maquinari certificats FIPS 140-3 nivell 3, dels quals el material de la clau no surt mai en clar.
  • Executa operacions criptogràfiques (Encrypt, Decrypt, GenerateDataKey, Sign, Verify) sobre dades petites.
  • Controla qui pot fer servir cada clau, mitjançant polítiques.
  • Registra cada crida a CloudTrail (05-03), inclòs qui va desxifrar què i quan.

KMS no és:

  • Un magatzem de secrets: per a contrasenyes i cadenes de connexió hi ha Secrets Manager (04-03).
  • Un servei per xifrar fitxers grans: hi ha un límit dur de 4 KB per crida a Encrypt.
  • Una autoritat de certificació: això és ACM, i ho aclarim al final.
  • Un lloc d'on puguis extreure la teva clau per endur-te-la. Amb les claus gestionades per KMS, el material no es pot exportar. És una garantia, no una limitació.

Aquesta última frase mereix aturar-s'hi: si el material no surt mai, com es xifren les dades? La resposta és el xifratge de sobre.

Tipus de clau

A KMS la unitat fonamental és la KMS key (abans anomenada CMK, Customer Master Key). Hi ha tres categories segons l'origen:

Tipus Qui la crea Política Rotació Cost Quan
Propietat d'AWS AWS, compartida entre clients No la veus AWS ho decideix Gratis Xifratge per defecte d'S3 (SSE-S3)
Gestionada per AWS (aws/s3, aws/rds, aws/ebs) AWS, una per servei i compte No editable Anual, obligatòria Gratis (sí les peticions) Començar de pressa, sense control fi
Gestionada pel client (CMK) Tu Editable Opcional, configurable 1 USD/mes Quan necessites controlar qui fa servir la clau

La diferència decisiva entre les dues últimes no és el preu, és el control:

  • Amb una clau gestionada per AWS no pots editar la política de clau. Qualsevol principal del teu compte amb permisos d'S3 podrà desxifrar. No la pots fer servir des d'un altre compte ni denegar-ne l'ús a un rol concret.
  • Amb una clau gestionada pel client decideixes exactament qui administra i qui fa servir, pots denegar-ne l'accés a un rol encara que tingui permisos d'S3, la pots compartir entre comptes i la pots desactivar, cosa que converteix totes les dades xifrades amb ella en il·legibles a l'instant. Això últim és un botó d'emergència molt potent.

Per a mercadofresco-copias-basedatos, que conté dades personals, la clau ha de ser gestionada pel client. Aquest és el paper d'alias/mercadofresco-datos.

I una nota sobre els àlies: un àlies és un punter mutable a una clau. Fer servir alias/mercadofresco-datos al codi en lloc de l'UUID permet canviar la clau subjacent sense tocar l'aplicació. Cada àlies és regional i no es pot repetir dins de la regió.

Claus multiregió, material importat i CloudHSM

Tres variants que convé conèixer encara que MercadoFresco no les faci servir avui:

Claus multiregió. Una clau primària a eu-west-1 i rèpliques en altres regions que comparteixen el mateix material criptogràfic i el mateix identificador (amb prefix mrk-). El que es xifra en una regió es desxifra en una altra sense crides entre regions. És imprescindible per a còpies de seguretat entre regions i per a recuperació davant de desastres. Trenca l'aïllament regional, així que només quan de debò calgui.

Material importat (BYOK). Portes el teu propi material de clau i KMS només el custodia. Es fa servir quan una normativa exigeix que l'organització generi les claus. Té una contrapartida seriosa: si perds la teva còpia del material i expira a KMS, les dades són irrecuperables. I no admet rotació automàtica.

Magatzem de claus personalitzat sobre CloudHSM. El material viu en un clúster de CloudHSM de la teva propietat, dedicat, amb inquilí únic. És el que exigeixen alguns entorns financers o del sector públic. Costa de l'ordre d'1,50 USD per hora i HSM —més de 1.000 USD al mes per HSM, i se'n recomanen dos—, així que és una decisió de compliment, no d'enginyeria. Només ho esmentem.

El xifratge de sobre

Aquest és el concepte central de la lliçó. Si el material de la clau no surt mai de KMS i Encrypt només admet 4 KB, com es xifra un snapshot de 200 GB?

La resposta és que les dades grans no viatgen mai a KMS. Es fan servir dos nivells de clau:

  • La clau mestra (la KMS key) viu a KMS i no en surt mai.
  • La clau de dades (data key) és una clau AES-256 d'un sol ús que KMS genera, lliura en dos formats i després oblida.
sequenceDiagram
    participant A as Aplicacio / S3
    participant K as KMS<br/>alias/mercadofresco-datos
    participant D as Disc

    Note over A,D: XIFRATGE
    A->>K: GenerateDataKey(KeyId, KeySpec=AES_256)
    K->>K: Genera una clau AES-256 aleatoria
    K-->>A: Plaintext (clau en clar)<br/>+ CiphertextBlob (la mateixa, xifrada<br/>amb la clau mestra)
    A->>A: Xifra els 200 GB amb Plaintext (AES local, rapid)
    A->>A: ESBORRA Plaintext de memoria
    A->>D: Desa: dades xifrades + CiphertextBlob juntes

Els passos, en paraules:

  1. L'aplicació demana a KMS una clau de dades. No envia les dades.
  2. KMS genera una clau AES-256 aleatòria i retorna dues versions de la mateixa clau: en clar (Plaintext) i xifrada amb la clau mestra (CiphertextBlob).
  3. L'aplicació xifra els 200 GB localment amb la versió en clar. Va a velocitat de CPU, sense xarxa pel mig.
  4. L'aplicació esborra de memòria la versió en clar. Aquest pas és el que fa que el sistema sigui segur.
  5. Desa juntes les dades xifrades i el CiphertextBlob. El blob és inútil per a qui no pugui cridar kms:Decrypt sobre la clau mestra.

Avantatges d'aquest disseny, tots conseqüència del mateix truc:

Avantatge Per què
Rendiment Les dades es xifren localment a velocitat de CPU, no de xarxa
Cost Una crida a KMS per fitxer (o menys), no per byte
Sense límit de mida El límit de 4 KB només afecta la clau de dades, que ocupa 32 bytes
Rotació barata Rotar la mestra no obliga a tornar a xifrar les dades (ho veiem més endavant)
Traçabilitat Cada GenerateDataKey i cada Decrypt queda a CloudTrail

El desxifratge, pas a pas

sequenceDiagram
    participant D as Disc
    participant A as Aplicacio / S3
    participant K as KMS

    Note over D,K: DESXIFRATGE
    D-->>A: dades xifrades + CiphertextBlob
    A->>K: Decrypt(CiphertextBlob)
    K->>K: Comprova la politica de clau<br/>i els permisos IAM de qui crida
    alt Autoritzat
        K-->>A: Plaintext (clau de dades en clar)
        A->>A: Desxifra les dades localment
        A->>A: ESBORRA Plaintext
    else No autoritzat
        K-->>A: AccessDeniedException
        Note over A: Les dades no es poden llegir<br/>encara que tinguis el fitxer
    end

Fixa't en el punt crític: per desxifrar no cal indicar quina clau es va fer servir. El CiphertextBlob porta a dins l'identificador de la clau mestra, i KMS la localitza sola. Per això Decrypt no necessita el paràmetre KeyId amb claus simètriques.

I fixa't en la branca else: qui tingui el fitxer però no permís sobre la clau es queda amb soroll. Aquest és el motiu que un snapshot xifrat compartit per error continuï sent segur.

Xifratge del costat del client i del costat del servidor

Del costat del servidor (SSE) Del costat del client (CSE)
Qui xifra AWS, en rebre la dada La teva aplicació, abans d'enviar-la
Què veu AWS La dada en clar durant el processament Només dades xifrades, mai en clar
Complexitat Gairebé cap: una casella Alta: gestiones el procés
Cerques, índexs Funcionen No funcionen sobre el que està xifrat
Quan El 95 % dels casos Confidencialitat extrema davant del proveïdor

MercadoFresco fa servir del costat del servidor en tot. El xifratge del costat del client té sentit quan el model d'amenaça inclou el mateix proveïdor de núvol o quan una normativa ho exigeix; per a una botiga de producte fresc, la complexitat addicional no compensa. Si algun dia calgués, existeix l'AWS Encryption SDK i el client xifrat d'S3, que implementen el sobre per tu.

SSE-S3, SSE-KMS i SSE-C

S3 ofereix tres formes de xifratge del costat del servidor. La diferència és qui controla la clau:

SSE-S3 SSE-KMS SSE-C
Clau mestra Propietat d'AWS Teva, a KMS Teva, l'envies a cada petició
Capçalera AES256 aws:kms aws:kms no; capçaleres -customer-
Política d'accés Només IAM d'S3 IAM d'S3 i política de clau La tens tu
Registre a CloudTrail No Sí, cada ús No
Cost Gratis 1 USD/mes + peticions Gratis a KMS
Rotació Automàtica, invisible Configurable i auditable Manual, teva
Doble autorització No No
Quan Dades no sensibles Dades personals, compliment Gairebé mai

La fila decisiva és doble autorització. Amb SSE-KMS, per llegir un objecte calen dos permisos: s3:GetObject i kms:Decrypt sobre la clau. Si un bucket amb dades personals quedés accidentalment exposat al públic, l'atacant obtindria l'objecte xifrat i no el podria obrir, perquè no té permís sobre la clau. És una xarxa de seguretat real que SSE-S3 no dona.

SSE-C, on tu envies la clau a cada petició i AWS la fa servir i l'oblida, obliga a gestionar la distribució de claus pel teu compte i no deixa registre. S'esmenta per completitud.

Decisió per a MercadoFresco:

Bucket Xifratge Motiu
mercadofresco-copias-basedatos SSE-KMS amb alias/mercadofresco-datos Dades personals, RGPD, auditoria
mercadofresco-catalogo-fotos SSE-KMS Coherència; ja ho exigeix la política de la botiga
mercadofresco-informes-analitica SSE-KMS Dades agregades de negoci
mercadofresco-registros-web SSE-S3 Volum alt, sense dades personals directes
mercadofresco-tienda-web-desarrollo SSE-S3 Contingut estàtic públic

Les claus de bucket d'S3 i el cost de les peticions

Aquí hi ha un parany econòmic que enfonsa pressupostos. Sense res més, cada objecte que es puja o es descarrega d'un bucket amb SSE-KMS genera una crida a KMS. mercadofresco-catalogo-fotos serveix —abans de CloudFront— de l'ordre de 2 milions de peticions al mes. A 0,03 USD per cada 10.000 peticions, això són 6 USD al mes només en crides a KMS, més que la mateixa clau.

La solució és la clau de bucket d'S3 (S3 Bucket Key): S3 demana a KMS una clau de nivell de bucket, la manté en memòria cau unes hores i en deriva les claus dels objectes individuals.

Sense clau de bucket Amb clau de bucket
Crides a KMS Una per objecte Una cada poques hores
Reducció de cost Fins al 99 %
Registre a CloudTrail Una entrada per objecte Una entrada per clau de bucket
Cost afegit Cap

La contrapartida és la tercera fila: perds la traça per objecte a CloudTrail. Per a mercadofresco-catalogo-fotos, amb milions de fotos públiques, és un intercanvi excel·lent. Per a mercadofresco-copias-basedatos, on vols saber exactament qui va desxifrar quina còpia i quan, pot interessar deixar-la desactivada: són poques peticions al mes i la traça val més que l'estalvi.

Activa-la sempre que no necessitis la traça per objecte. És gratis i estalvia un 99 %.

La política de clau i la seva relació amb IAM

Aquí hi ha l'excepció que vam anunciar a 04-01. A gairebé tots els serveis, al mateix compte n'hi ha prou que una de les dues polítiques concedeixi. A KMS no. Tota clau té una política de clau (una política basada en recurs) i:

Si la política de clau no ho permet, ningú no pot fer servir la clau. Ni l'administrador del compte, ni tan sols el root.

Això té una conseqüència que cal gravar-se a foc: si crees una clau per CLI amb una política que no inclou ningú, la clau queda inservible per sempre i només se'n pot programar l'eliminació. AWS ho evita a la consola, però no per CLI.

Hi ha dos models:

  1. La política de clau delega en IAM. S'hi inclou la declaració "Principal": {"AWS": "arn:aws:iam::111122223333:root"} amb "Action": "kms:*", cosa que significa «les polítiques d'IAM d'aquest compte poden concedir accés a aquesta clau». És el que fa la consola per defecte.
  2. La política de clau concedeix directament, nomenant rols concrets. Més restrictiu i més explícit.

MercadoFresco fa servir un model mixt: delega en IAM per a l'administració però nomena explícitament qui fa servir la clau. Aquesta és la política completa d'alias/mercadofresco-datos:

{
  "Version": "2012-10-17",
  "Id": "politica-mercadofresco-datos",
  "Statement": [
    {
      "Sid": "PermitirQueIAMGobierneLaClave",
      "Effect": "Allow",
      "Principal": { "AWS": "arn:aws:iam::111122223333:root" },
      "Action": "kms:*",
      "Resource": "*"
    },
    {
      "Sid": "MartaAdministraLaClavePeroNoLaUsa",
      "Effect": "Allow",
      "Principal": { "AWS": "arn:aws:iam::111122223333:user/marta" },
      "Action": [
        "kms:Create*", "kms:Describe*", "kms:Enable*", "kms:List*", "kms:Put*",
        "kms:Update*", "kms:Revoke*", "kms:Disable*", "kms:Get*", "kms:Delete*",
        "kms:TagResource", "kms:UntagResource", "kms:ScheduleKeyDeletion", "kms:CancelKeyDeletion"
      ],
      "Resource": "*"
    },
    {
      "Sid": "LosRolesDeAplicacionUsanLaClave",
      "Effect": "Allow",
      "Principal": {
        "AWS": [
          "arn:aws:iam::111122223333:role/rol-mercadofresco-tienda",
          "arn:aws:iam::111122223333:role/rol-lambda-miniaturas"
        ]
      },
      "Action": [
        "kms:Encrypt", "kms:Decrypt", "kms:ReEncrypt*",
        "kms:GenerateDataKey*", "kms:DescribeKey"
      ],
      "Resource": "*",
      "Condition": {
        "StringEquals": {
          "kms:ViaService": ["s3.eu-west-1.amazonaws.com"]
        }
      }
    },
    {
      "Sid": "PermitirQueRDSYEBSUsenLaClaveMedianteConcesiones",
      "Effect": "Allow",
      "Principal": { "AWS": "arn:aws:iam::111122223333:root" },
      "Action": "kms:CreateGrant",
      "Resource": "*",
      "Condition": {
        "Bool": { "kms:GrantIsForAWSResource": "true" },
        "StringEquals": {
          "kms:ViaService": ["rds.eu-west-1.amazonaws.com", "ec2.eu-west-1.amazonaws.com"]
        }
      }
    },
    {
      "Sid": "ProhibirElUsoFueraDeEuWest1",
      "Effect": "Deny",
      "Principal": "*",
      "Action": "kms:*",
      "Resource": "*",
      "Condition": {
        "StringNotEquals": { "aws:RequestedRegion": "eu-west-1" }
      }
    }
  ]
}

Declaració per declaració:

  • PermitirQueIAMGobierneLaClave: l'«interruptor» que fa que les polítiques d'IAM comptin per a aquesta clau. Sense ell, cap política d'identitat no hi tindria efecte. És el que evita quedar-te fora de la teva pròpia clau.
  • MartaAdministraLaClavePeroNoLaUsa: la Marta pot habilitar, deshabilitar, etiquetar, canviar la política i programar l'eliminació... però no hi apareix kms:Decrypt ni kms:Encrypt. Aquesta és la separació de funcions: qui administra la clau no pot llegir les dades que protegeix. És un requisit habitual en auditories i aquí es veu implementat en dues línies.
  • LosRolesDeAplicacionUsanLaClave: els dos rols que vam formalitzar a 04-01 poden xifrar i desxifrar, però només a través d'S3 gràcies a kms:ViaService. Si algú roba les credencials de la instància i crida kms:Decrypt directament contra un blob que va aconseguir per una altra via, KMS ho rebutja.
  • PermitirQueRDSYEBSUsenLaClaveMedianteConcesiones: RDS i EBS no criden la clau directament; creen concessions en nom teu. La condició kms:GrantIsForAWSResource limita aquesta capacitat als serveis d'AWS, no a persones.
  • ProhibirElUsoFueraDeEuWest1: barrera de residència de dades. Encaixa amb l'argumentació de RGPD sobre mantenir el tractament dins de la UE.

I el rol de la botiga, a més, necessita a la seva política d'identitat els mateixos permisos de KMS —recorda: al mateix compte n'hi ha prou que una concedeixi, llevat que la de recurs denegui, però a KMS la de recurs sempre ha de permetre. A la pràctica s'escriuen totes dues.

kms:ViaService i el context de xifratge

Dos mecanismes que refinen el control i que apareixen constantment en polítiques reals.

kms:ViaService. Restringeix l'ús de la clau a peticions que arriben a través d'un servei concret, i només quan el servei actua en nom del principal. Valors típics: s3.eu-west-1.amazonaws.com, rds.eu-west-1.amazonaws.com, ec2.eu-west-1.amazonaws.com, secretsmanager.eu-west-1.amazonaws.com. És la condició més útil de KMS: converteix una clau genèrica en una clau «només per a S3».

Context de xifratge (encryption context). Un diccionari de parells clau-valor que s'associa a una operació de xifratge. No és secret —apareix als registres de CloudTrail en clar— però està autenticat: si en desxifrar no proporciones exactament el mateix context, l'operació falla.

context = {"proyecto": "mercadofresco", "componente": "copias", "entorno": "produccion"}

Serveix per a dues coses:

  1. Auditoria llegible. A CloudTrail hi veus «s'ha desxifrat alguna cosa del component copias» en lloc d'un UUID opac.
  2. Autorització granular. Es pot condicionar la política de clau a un context concret:
{
  "Sid": "SoloDescifrarCopias",
  "Effect": "Allow",
  "Principal": { "AWS": "arn:aws:iam::111122223333:role/rol-restauracion" },
  "Action": "kms:Decrypt",
  "Resource": "*",
  "Condition": {
    "StringEquals": { "kms:EncryptionContext:componente": "copias" }
  }
}

Aquell rol pot desxifrar còpies de la base de dades i res més, encara que tot estigui xifrat amb la mateixa clau. S3 fa servir automàticament com a context l'ARN del bucket o de l'objecte, així que ja te'n beneficies sense fer res.

Concessions (grants)

Una concessió és un permís temporal i granular sobre una clau, pensat per a serveis i processos, no per a persones:

Política de clau Concessió
Format JSON Crida a l'API
Durada Permanent fins que s'editi Es retira amb RetireGrant
Granularitat Principal + acció + condició Principal + operacions concretes + context
Qui les fa servir Humans i rols Serveis d'AWS, sobretot

Quan crees una instància d'RDS xifrada, RDS crea una concessió sobre la clau per poder desxifrar el volum a cada arrencada, a cada failover i a cada restauració de snapshot. Per això la política de clau havia de permetre kms:CreateGrant amb kms:GrantIsForAWSResource.

Conseqüència pràctica molt important: si retires aquella concessió, la base de dades deixa d'arrencar. I si la concessió es retira mentre la base està aturada, no tornarà. No toquis les concessions que creen els serveis.

aws kms list-grants --key-id alias/mercadofresco-datos \
  --query 'Grants[].[GranteePrincipal,Operations[0],Name]' --output table \
  --profile mercadofresco-dev

Rotació automàtica i què significa realment

Es pot activar la rotació anual amb una crida:

aws kms enable-key-rotation --key-id alias/mercadofresco-datos --profile mercadofresco-dev
aws kms get-key-rotation-status --key-id alias/mercadofresco-datos --profile mercadofresco-dev

I ara la part que gairebé tothom entén malament. Quan KMS rota una clau:

  • Genera material criptogràfic nou i l'associa a la mateixa clau lògica i al mateix ARN.
  • Conserva tot el material anterior, indefinidament.
  • El material nou es fa servir per a les operacions noves de xifratge.
  • Les dades ja xifrades no es tornen a xifrar. Continuen fent servir el seu material antic, que KMS localitza sol.

És a dir: la rotació és transparent i gratuïta, però no «rejoveneix» les dades antigues. Si un atacant hagués obtingut d'alguna manera el material antic, les dades xifrades amb ell continuen compromeses. Per renovar de debò les dades cal tornar-les a xifrar, copiant els objectes sobre si mateixos o fent servir ReEncrypt.

Rotació automàtica de KMS Rexifratge real de les dades
Què canvia El material de les operacions noves Les dades ja desades
Cost Gratis Peticions a KMS + temps
Compliment Cobreix «rotació anual de claus» Cobreix «renovació després d'un incident»
Esforç Una ordre Un procés per lots

Des del 2024 el període de rotació és configurable entre 90 i 2.560 dies amb --rotation-period-in-days. Per a MercadoFresco, la rotació anual per defecte és suficient i és el que la majoria d'auditories esperen veure.

Xifrar mercadofresco-copias-basedatos

Primer, crear la clau amb la política de l'apartat anterior:

aws kms create-key \
  --description "Clau de dades personals de MercadoFresco (RGPD)" \
  --key-usage ENCRYPT_DECRYPT \
  --key-spec SYMMETRIC_DEFAULT \
  --policy file:///tmp/politica-clave.json \
  --tags TagKey=Proyecto,TagValue=mercadofresco TagKey=Entorno,TagValue=produccion \
         TagKey=Componente,TagValue=datos TagKey=Propietario,TagValue=marta \
         TagKey=CentroCoste,TagValue=tecnologia \
  --profile mercadofresco-dev

# Retorna KeyId: 1234abcd-12ab-34cd-56ef-1234567890ab
aws kms create-alias \
  --alias-name alias/mercadofresco-datos \
  --target-key-id 1234abcd-12ab-34cd-56ef-1234567890ab \
  --profile mercadofresco-dev

aws kms enable-key-rotation --key-id alias/mercadofresco-datos --profile mercadofresco-dev

Després, xifratge per defecte al bucket. Fixa't que aquí no activem la clau de bucket, perquè volem la traça per objecte:

aws s3api put-bucket-encryption \
  --bucket mercadofresco-copias-basedatos \
  --server-side-encryption-configuration '{
    "Rules": [{
      "ApplyServerSideEncryptionByDefault": {
        "SSEAlgorithm": "aws:kms",
        "KMSMasterKeyID": "arn:aws:kms:eu-west-1:111122223333:alias/mercadofresco-datos"
      },
      "BucketKeyEnabled": false
    }]
  }' \
  --profile mercadofresco-dev

Al catàleg de fotos, en canvi, sí que l'activem:

aws s3api put-bucket-encryption \
  --bucket mercadofresco-catalogo-fotos \
  --server-side-encryption-configuration '{
    "Rules": [{
      "ApplyServerSideEncryptionByDefault": {
        "SSEAlgorithm": "aws:kms",
        "KMSMasterKeyID": "arn:aws:kms:eu-west-1:111122223333:alias/mercadofresco-datos"
      },
      "BucketKeyEnabled": true
    }]
  }' \
  --profile mercadofresco-dev

I finalment, la política de bucket que rebutja qualsevol pujada sense xifrar, perquè el xifratge per defecte no depengui que ningú no el desactivi:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "RechazarSubidasSinKMS",
      "Effect": "Deny",
      "Principal": "*",
      "Action": "s3:PutObject",
      "Resource": "arn:aws:s3:::mercadofresco-copias-basedatos/*",
      "Condition": {
        "StringNotEquals": {
          "s3:x-amz-server-side-encryption": "aws:kms"
        }
      }
    },
    {
      "Sid": "RechazarOtraClave",
      "Effect": "Deny",
      "Principal": "*",
      "Action": "s3:PutObject",
      "Resource": "arn:aws:s3:::mercadofresco-copias-basedatos/*",
      "Condition": {
        "StringNotEqualsIfExists": {
          "s3:x-amz-server-side-encryption-aws-kms-key-id":
            "arn:aws:kms:eu-west-1:111122223333:key/1234abcd-12ab-34cd-56ef-1234567890ab"
        }
      }
    }
  ]
}

Important: el xifratge per defecte no xifra retroactivament. Els objectes que ja eren al bucket continuen com estaven. Per xifrar-los cal copiar-los sobre si mateixos:

aws s3 cp s3://mercadofresco-copias-basedatos/ s3://mercadofresco-copias-basedatos/ \
  --recursive --sse aws:kms \
  --sse-kms-key-id arn:aws:kms:eu-west-1:111122223333:alias/mercadofresco-datos \
  --metadata-directive REPLACE \
  --profile mercadofresco-dev

Amb versionatge actiu això crea una versió nova xifrada i deixa l'antiga sense xifrar. Cal eliminar les versions antigues després, o la feina no serveix de res. És un detall que s'oblida sovint i que una auditoria detecta de seguida.

Xifrar volums d'EBS i snapshots

El més senzill: activar el xifratge per defecte d'EBS a la regió. A partir d'aquell moment, tot volum nou es xifra sense que ningú se n'hagi de recordar.

aws ec2 enable-ebs-encryption-by-default --region eu-west-1 --profile mercadofresco-dev
aws ec2 modify-ebs-default-kms-key-id \
  --kms-key-id alias/mercadofresco-datos --region eu-west-1 --profile mercadofresco-dev
aws ec2 get-ebs-default-kms-key-id --region eu-west-1 --profile mercadofresco-dev

Per a un volum ja existent sense xifrar —com mercadofresco-fotos-01, creat a 02-02— no es pot xifrar en calent. El procediment és snapshot, còpia xifrada, volum nou:

# 1. Snapshot del volum sense xifrar
aws ec2 create-snapshot --volume-id vol-0abc123 \
  --description "mercadofresco-fotos-01 previ al xifratge" \
  --profile mercadofresco-dev

# 2. Copiar el snapshot XIFRANT durant el procés
aws ec2 copy-snapshot \
  --source-region eu-west-1 --source-snapshot-id snap-0abc123 \
  --encrypted --kms-key-id alias/mercadofresco-datos \
  --description "mercadofresco-fotos-01 xifrat" \
  --profile mercadofresco-dev

# 3. Crear el volum nou des del snapshot xifrat
aws ec2 create-volume --snapshot-id snap-0def456 \
  --availability-zone eu-west-1a --volume-type gp3 \
  --tag-specifications 'ResourceType=volume,Tags=[{Key=Name,Value=mercadofresco-fotos-01},
      {Key=Proyecto,Value=mercadofresco},{Key=Entorno,Value=produccion}]' \
  --profile mercadofresco-dev

# 4. Aturar la instància, desmuntar el vell, muntar el nou, arrencar

El truc és al pas 2: copy-snapshot --encrypted és l'única operació d'EBS que converteix una cosa sense xifrar en una cosa xifrada. Recorda-ho, perquè és la peça que falta a gairebé tots els intents.

I una regla que estalvia ensurts: d'un volum xifrat només en surten snapshots xifrats, i d'un snapshot xifrat només en surten volums xifrats. La propietat s'hereta i no es pot treure.

Xifrar mercadofresco-pedidos, que ja existeix sense xifrar

Aquesta és l'operació més delicada del mòdul, perquè implica temps d'inactivitat de la base de dades de producció. RDS no permet xifrar una instància existent. El camí és:

flowchart TD
    A["mercadofresco-pedidos<br/>SENSE xifrar (produccio)"] --> B["1. Snapshot manual<br/>mercadofresco-pedidos-precifrado"]
    B --> C["2. copy-db-snapshot --kms-key-id<br/>alias/mercadofresco-datos"]
    C --> D["3. restore-db-instance-from-db-snapshot<br/>mercadofresco-pedidos-cifrada"]
    D --> E["4. Verificar: dades, parametres,<br/>grup de subxarxes, SG"]
    E --> F["5. Finestra de manteniment:<br/>aturar escriptures"]
    F --> G["6. Reanomenar: la vella passa a -antigua,<br/>la nova a mercadofresco-pedidos"]
    G --> H["7. Reactivar Multi-AZ i la replica<br/>mercadofresco-pedidos-lectura"]
    H --> I["8. Conservar la vella 7 dies,<br/>despres eliminar"]
# 1 i 2
aws rds create-db-snapshot \
  --db-instance-identifier mercadofresco-pedidos \
  --db-snapshot-identifier mercadofresco-pedidos-precifrado \
  --profile mercadofresco-dev

aws rds copy-db-snapshot \
  --source-db-snapshot-identifier mercadofresco-pedidos-precifrado \
  --target-db-snapshot-identifier mercadofresco-pedidos-cifrado \
  --kms-key-id alias/mercadofresco-datos \
  --profile mercadofresco-dev

# 3
aws rds restore-db-instance-from-db-snapshot \
  --db-instance-identifier mercadofresco-pedidos-cifrada \
  --db-snapshot-identifier mercadofresco-pedidos-cifrado \
  --db-subnet-group-name sng-mercadofresco-datos \
  --db-parameter-group-name pg16-mercadofresco \
  --multi-az \
  --no-publicly-accessible \
  --profile mercadofresco-dev

Quatre avisos de camp:

  • La restauració no conserva el grup de paràmetres, els grups de seguretat ni la configuració Multi-AZ. Cal indicar-los explícitament, com a l'ordre de dalt, o la instància nova arrencarà amb els valors per defecte i quedarà mal configurada.
  • El nom del punt d'enllaç canvia. Si l'aplicació apunta al DNS d'RDS, cal actualitzar-lo. Per això a 03-05 vam crear la zona privada interno.mercadofresco.example: si la botiga es connecta a pedidos.interno.mercadofresco.example, n'hi ha prou amb canviar un CNAME.
  • La rèplica mercadofresco-pedidos-lectura no es migra. Cal recrear-la des de la instància nova.
  • Fes l'assaig complet a desenvolupament abans. La Marta fa aquesta migració un dimarts a la nit, mai un dijous: el pic dels divendres no és moment per descobrir sorpreses.

Compartir un snapshot xifrat amb un altre compte

Un snapshot xifrat amb la clau gestionada per AWS (aws/rds) no es pot compartir. Només es comparteixen els xifrats amb una clau gestionada pel client, i calen dos permisos:

# 1. Compartir el snapshot amb el compte de destinació
aws rds modify-db-snapshot-attribute \
  --db-snapshot-identifier mercadofresco-pedidos-cifrado \
  --attribute-name restore --values-to-add 444455556666 \
  --profile mercadofresco-dev
{
  "Sid": "PermitirQueLaCuentaDeAuditoriaDescifreElSnapshot",
  "Effect": "Allow",
  "Principal": { "AWS": "arn:aws:iam::444455556666:root" },
  "Action": ["kms:Decrypt", "kms:DescribeKey", "kms:CreateGrant"],
  "Resource": "*",
  "Condition": {
    "StringEquals": { "kms:ViaService": "rds.eu-west-1.amazonaws.com" }
  }
}

És a dir: compartir el snapshot i donar accés a la clau. Si només fas el primer, l'altre compte veurà el snapshot i no el podrà restaurar. I no oblidis que el compte de destinació també necessita permetre-ho a les seves pròpies polítiques d'identitat.

Aquest és, dit sigui de passada, el mecanisme que fa segura l'exposició accidental: compartir un snapshot sense compartir la clau no revela absolutament res.

KMS des de boto3

El cas d'ús típic —xifrar un fitxer de mida arbitrària amb el sobre, a mà:

import boto3
import os
from cryptography.fernet import Fernet   # nomes per a l'exemple local
import base64

sessio = boto3.Session(profile_name="mercadofresco-dev", region_name="eu-west-1")
kms = sessio.client("kms")

CLAU = "alias/mercadofresco-datos"
CONTEXT = {"proyecto": "mercadofresco", "componente": "copias"}


def xifrar_fitxer(ruta_origen: str, ruta_desti: str) -> None:
    """Xifra un fitxer de qualsevol mida fent servir xifratge de sobre."""
    # 1. Demanar una clau de dades: arriben les DUES versions
    resposta = kms.generate_data_key(
        KeyId=CLAU,
        KeySpec="AES_256",
        EncryptionContext=CONTEXT,
    )
    clau_en_clar = resposta["Plaintext"]         # 32 bytes, per xifrar aqui
    clau_xifrada = resposta["CiphertextBlob"]    # es desa al costat del fitxer

    # 2. Xifrar localment. Les dades NO surten mai cap a KMS.
    f = Fernet(base64.urlsafe_b64encode(clau_en_clar))
    with open(ruta_origen, "rb") as origen:
        dades_xifrades = f.encrypt(origen.read())

    # 3. Desar la clau xifrada + les dades xifrades al mateix fitxer
    with open(ruta_desti, "wb") as desti:
        desti.write(len(clau_xifrada).to_bytes(4, "big"))
        desti.write(clau_xifrada)
        desti.write(dades_xifrades)

    # 4. Esborrar de memoria la versio en clar
    del clau_en_clar


def desxifrar_fitxer(ruta_origen: str, ruta_desti: str) -> None:
    with open(ruta_origen, "rb") as origen:
        longitud = int.from_bytes(origen.read(4), "big")
        clau_xifrada = origen.read(longitud)
        dades_xifrades = origen.read()

    # KMS dedueix la clau mestra del propi blob: no cal KeyId
    resposta = kms.decrypt(
        CiphertextBlob=clau_xifrada,
        EncryptionContext=CONTEXT,   # ha de coincidir EXACTAMENT
    )
    f = Fernet(base64.urlsafe_b64encode(resposta["Plaintext"]))

    with open(ruta_desti, "wb") as desti:
        desti.write(f.decrypt(dades_xifrades))


# Xifratge directe, nomes per a dades de menys de 4 KB
petit = kms.encrypt(KeyId=CLAU, Plaintext=b"dada curta", EncryptionContext=CONTEXT)
print(kms.decrypt(CiphertextBlob=petit["CiphertextBlob"],
                  EncryptionContext=CONTEXT)["Plaintext"])

Punts que cal fixar de l'exemple:

  • generate_data_key retorna les dues versions de la mateixa clau. Aquesta és l'essència del sobre.
  • El fitxer xifrat és autocontingut: porta a dins la clau xifrada. Es pot moure a qualsevol lloc i només l'obrirà qui tingui permís sobre alias/mercadofresco-datos.
  • decrypt no rep KeyId: el blob el porta a dins.
  • L'EncryptionContext ha de coincidir caràcter a caràcter en desxifrar. Si no, InvalidCiphertext.
  • En producció no es fa servir Fernet a mà: l'AWS Encryption SDK fa tot això correctament, inclosos l'esborrat segur de memòria i el format de missatge. L'exemple és per veure la mecànica.

Límits, quotes i cost real per a MercadoFresco

Concepte Preu
Clau gestionada pel client 1,00 USD per clau i mes (prorratejat)
Peticions simètriques 0,03 USD per cada 10.000
Peticions RSA-2048 0,03 USD per cada 10.000
Peticions ECC / RSA >2048 0,15 USD per cada 10.000
Claus gestionades i propietat d'AWS Gratis (les peticions que es cobren)
Capa gratuïta 20.000 peticions al mes, sempre
Magatzem personalitzat (CloudHSM) ~1,50 USD/hora per HSM, a part

Quotes de peticions per segon a eu-west-1: de l'ordre de 50.000 per a Decrypt, GenerateDataKey i Encrypt simètrics, i molt inferiors (uns quants centenars) per a operacions asimètriques. Es comparteixen entre totes les claus del compte, així que un procés per lots agressiu pot provocar ThrottlingException a tot el compte. Els SDK reintenten amb retrocés exponencial, però convé saber-ho.

Càlcul per a MercadoFresco:

Concepte Quantitat Cost mensual
Clau alias/mercadofresco-datos 1 1,00 USD
mercadofresco-catalogo-fotos amb clau de bucket ~2.000.000 peticions → ~700 crides 0,00 USD (capa gratuïta)
mercadofresco-copias-basedatos sense clau de bucket 60 còpies × 2 (pujar + verificar) = 120 0,00 USD
EBS i RDS (concessions i arrencades) ~500 0,00 USD
Total 1,00 USD/mes

Un dòlar al mes per xifrar totes les dades personals de l'empresa, amb auditoria de cada ús. És, de molt, la millor relació cost-benefici de tot el curs.

El contrast és útil: sense la clau de bucket, aquells 2 milions de peticions haurien generat 2 milions de crides a KMS, uns 6 USD al mes. No són diners, però a escala de 100 milions de peticions serien 300 USD mensuals per no haver marcat una casella.

ACM no és KMS

Confusió freqüent, perquè tots dos parlen de claus:

AWS KMS AWS Certificate Manager
Protegeix Dades en repòs Dades en trànsit
Objecte Claus de xifratge simètriques Certificats X.509
Cost 1 USD/clau/mes Gratis per a certificats públics
A MercadoFresco alias/mercadofresco-datos Certificats de l'ALB i de CloudFront (03-03, 03-05)
Renovació Rotació anual del material Renovació automàtica via DNS

Són serveis independents que resolen meitats diferents del mateix problema. El certificat de mercadofresco.example que vam validar amb un CNAME a 03-05 no té res a veure amb alias/mercadofresco-datos.

Eliminar una clau: els 7 a 30 dies d'espera

No es pot esborrar una clau de KMS de manera immediata, i és deliberat: esborrar-la destruiria de manera irreversible totes les dades xifrades amb ella. Només se'n pot programar l'eliminació amb un període d'espera d'entre 7 i 30 dies.

# Abans de res: la fa servir algú? Mira't CloudTrail (05-03)
aws kms schedule-key-deletion \
  --key-id alias/mercadofresco-datos --pending-window-in-days 30 \
  --profile mercadofresco-dev

# Cancel·lar mentre estigui pendent
aws kms cancel-key-deletion --key-id 1234abcd-12ab-34cd-56ef-1234567890ab \
  --profile mercadofresco-dev

Una alternativa molt més assenyada per provar l'impacte és deshabilitar la clau: és reversible a l'instant i produeix exactament el mateix efecte que esborrar-la —tot el que està xifrat deixa de poder-se llegir—, però sense destruir res.

aws kms disable-key --key-id alias/mercadofresco-datos --profile mercadofresco-dev
aws kms enable-key  --key-id alias/mercadofresco-datos --profile mercadofresco-dev

Deshabilitar durant 30 dies, comprovar que res no es trenca i només llavors programar l'eliminació: aquest és el procediment correcte. I mentre la clau estigui deshabilitada es continua pagant el seu dòlar mensual.

Errors Habituals i Consells

Escriure una política de clau que deixa tothom fora. És l'error irreversible de KMS. Si crees la clau per CLI amb una política que no inclou "Principal": {"AWS": "arn:aws:iam::111122223333:root"} amb kms:*, la clau queda inservible i només se'n pot programar l'eliminació. Inclou-hi sempre aquella declaració.

Creure que el xifratge per defecte d'un bucket xifra el que ja hi havia. No ho fa. Els objectes existents continuen sense xifrar fins que els copiïs sobre si mateixos, i amb versionatge actiu cal purgar a més les versions antigues.

Oblidar kms:Decrypt al rol. El símptoma és confús: s3:GetObject està concedit, l'objecte existeix i tot i així arriba AccessDenied. Amb SSE-KMS calen dos permisos. La primera comprovació davant d'un AccessDenied en un bucket xifrat és sempre KMS.

No activar la clau de bucket en buckets de molt trànsit. Multiplica el cost de KMS pel nombre de peticions. Activa-la llevat que necessitis la traça per objecte.

Fer servir Encrypt per a fitxers grans. Falla amb ValidationException a partir de 4 KB. Per a això hi ha GenerateDataKey i el sobre.

Confondre deshabilitar amb eliminar. Deshabilitar és reversible i continua costant 1 USD al mes; eliminar és irreversible i destrueix les dades. Comença sempre deshabilitant.

Retirar una concessió creada per RDS o EBS. La base de dades deixa d'arrencar. Les concessions que creen els serveis no es toquen.

Consell: fes servir àlies, mai UUID, al codi. alias/mercadofresco-datos és llegible i permet canviar la clau subjacent sense desplegar. A les polítiques, en canvi, fes servir l'ARN de clau: és inequívoc i no depèn d'on apunti l'àlies.

Consell: aplica separació de funcions des del primer dia. Que qui administra la clau no pugui desxifrar les dades, com a la política d'alias/mercadofresco-datos. Costa dues línies i és el primer que pregunta un auditor.

Consell: activa el xifratge per defecte d'EBS a la regió. Una ordre, gratis, i a partir d'aquí ningú no pot crear un volum sense xifrar per descuit.

Consell: posa una alarma sobre les crides a kms:Decrypt. Un pic anòmal de desxifratges és un dels senyals més primerencs d'una exfiltració de dades. El muntarem amb CloudWatch a 05-01 sobre els esdeveniments de CloudTrail de 05-03.

Exercicis

Exercici 1: dissenyar la política d'una clau nova

MercadoFresco crea una segona clau, alias/mercadofresco-analitica, per xifrar mercadofresco-informes-analitica. Requisits:

  • La Marta administra la clau però no pot desxifrar.
  • La Sara (usuària sara) pot desxifrar informes, però només a través d'S3 i només si el context de xifratge inclou componente=informes.
  • Un rol rol-mercadofresco-etl pot xifrar i desxifrar sense restricció de context, també només a través d'S3.
  • Ningú no pot fer servir la clau fora d'eu-west-1.
  • Les polítiques d'IAM del compte han de poder concedir accés.

Escriu la política de clau completa.

Exercici 2: calcular el cost i decidir sobre la clau de bucket

MercadoFresco obre a Portugal i el catàleg passa a servir 40 milions de peticions al mes, de les quals CloudFront n'absorbeix el 90 % (només el 10 % arriba a S3). A més, el bucket mercadofresco-copias-basedatos rep 3 còpies diàries i mercadofresco-informes-analitica genera 50.000 objectes al mes.

Calcula el cost mensual de KMS en dos escenaris —amb i sense clau de bucket al catàleg— fent servir 2 claus gestionades pel client, i decideix quina configuració recomanaries per a cada bucket justificant el compromís entre cost i traçabilitat.

Exercici 3: diagnosticar un xifratge que falla

El Luis desplega una versió nova de la botiga. Les fotos es llegeixen bé, però en generar una miniatura la funció mercadofresco-generar-miniaturas falla amb:

An error occurred (AccessDenied) when calling the PutObject operation:
User: arn:aws:sts::111122223333:assumed-role/rol-lambda-miniaturas/mercadofresco-generar-miniaturas
is not authorized to perform: kms:GenerateDataKey on resource:
arn:aws:kms:eu-west-1:111122223333:key/1234abcd-12ab-34cd-56ef-1234567890ab
because no identity-based policy allows the kms:GenerateDataKey action

Respon: (a) per què falla en escriure si en llegir funcionava?; (b) en quins dos llocs diferents cal mirar i en quin és el problema segons el missatge?; (c) escriu la correcció mínima; (d) si l'error hagués dit with an explicit deny in a resource-based policy, què sospitaries, sabent que la política de clau inclou kms:ViaService?

Solucions

Solució 1

{
  "Version": "2012-10-17",
  "Id": "politica-mercadofresco-analitica",
  "Statement": [
    {
      "Sid": "PermitirQueIAMGobierneLaClave",
      "Effect": "Allow",
      "Principal": { "AWS": "arn:aws:iam::111122223333:root" },
      "Action": "kms:*",
      "Resource": "*"
    },
    {
      "Sid": "MartaAdministra",
      "Effect": "Allow",
      "Principal": { "AWS": "arn:aws:iam::111122223333:user/marta" },
      "Action": [
        "kms:Describe*", "kms:Get*", "kms:List*", "kms:Enable*", "kms:Disable*",
        "kms:Update*", "kms:Put*", "kms:Revoke*", "kms:TagResource", "kms:UntagResource",
        "kms:ScheduleKeyDeletion", "kms:CancelKeyDeletion"
      ],
      "Resource": "*"
    },
    {
      "Sid": "SaraDescifraSoloInformes",
      "Effect": "Allow",
      "Principal": { "AWS": "arn:aws:iam::111122223333:user/sara" },
      "Action": ["kms:Decrypt", "kms:DescribeKey"],
      "Resource": "*",
      "Condition": {
        "StringEquals": {
          "kms:ViaService": "s3.eu-west-1.amazonaws.com",
          "kms:EncryptionContext:componente": "informes"
        }
      }
    },
    {
      "Sid": "ElProcesoETLCifraYDescifra",
      "Effect": "Allow",
      "Principal": { "AWS": "arn:aws:iam::111122223333:role/rol-mercadofresco-etl" },
      "Action": ["kms:Encrypt", "kms:Decrypt", "kms:GenerateDataKey*", "kms:DescribeKey"],
      "Resource": "*",
      "Condition": {
        "StringEquals": { "kms:ViaService": "s3.eu-west-1.amazonaws.com" }
      }
    },
    {
      "Sid": "ProhibirFueraDeEuWest1",
      "Effect": "Deny",
      "Principal": "*",
      "Action": "kms:*",
      "Resource": "*",
      "Condition": {
        "StringNotEquals": { "aws:RequestedRegion": "eu-west-1" }
      }
    }
  ]
}

Punts clau: a la declaració de la Marta no hi apareixen kms:Encrypt, kms:Decrypt ni kms:GenerateDataKey —separació de funcions—; la Sara només té Decrypt, mai Encrypt, i amb doble condició; i el Deny final s'aplica a "Principal": "*", és a dir, també a la Marta i al root.

Solució 2

Peticions a KMS per bucket:

  • Catàleg: 40.000.000 × 10 % = 4.000.000 peticions a S3 al mes.
    • Sense clau de bucket: 4.000.000 crides a KMS.
    • Amb clau de bucket: aproximadament una cada poques hores, de l'ordre de 250 al mes.
  • Còpies: 3 diàries × 30 = 90 objectes → ~180 crides (escriptura + verificació).
  • Informes: 50.000 objectes → 50.000 crides sense clau de bucket, ~250 amb ella.

Escenari A, sense clau de bucket enlloc:

  • Peticions: 4.000.000 + 180 + 50.000 = 4.050.180. Menys 20.000 de capa gratuïta = 4.030.180.
  • Cost: 4.030.180 ÷ 10.000 × 0,03 = 12,09 USD
  • Claus: 2 × 1,00 = 2,00 USD
  • Total: 14,09 USD/mes

Escenari B, amb clau de bucket a catàleg i informes, sense ella a còpies:

  • Peticions: 250 + 180 + 250 = 680, molt per sota de la capa gratuïta de 20.000.
  • Cost de peticions: 0,00 USD
  • Claus: 2,00 USD
  • Total: 2,00 USD/mes

Recomanació. Estalvi de 12,09 USD al mes, uns 145 USD l'any, i a més escala creix linealment. Configuració proposada:

Bucket Clau de bucket Justificació
mercadofresco-catalogo-fotos Milions de peticions, fotos públiques, la traça per objecte no aporta res
mercadofresco-informes-analitica 50.000 objectes al mes, dades agregades sense identificadors personals
mercadofresco-copias-basedatos No 90 objectes al mes: l'estalvi és zero i la traça de qui va desxifrar cada còpia de dades personals és exactament el que demanarà una auditoria de RGPD

El raonament general: activa la clau de bucket llevat que el volum sigui baix i la traçabilitat per objecte tingui valor de compliment.

Solució 3

(a) Perquè llegir i escriure requereixen permisos diferents de KMS. Descarregar un objecte xifrat amb SSE-KMS necessita kms:Decrypt; pujar-ne un de nou necessita kms:GenerateDataKey, perquè S3 ha de demanar una clau de dades nova per xifrar-lo. A 04-01 vam concedir tots dos a rol-lambda-miniaturas, així que aquí el que ha passat és que algú ha desplegat una versió de la política amb només kms:Decrypt, o el bucket ha canviat de clau.

(b) Cal mirar en dos llocs: la política d'identitat del rol i la política de clau d'alias/mercadofresco-datos. El missatge ho resol: because no identity-based policy allows assenyala inequívocament la política d'identitat del rol, i a més indica denegació implícita —falta un Allow—, no un Deny explícit.

(c) Afegir a rol-lambda-miniaturas l'acció que falta:

{
  "Sid": "UsarLaClaveDelBucket",
  "Effect": "Allow",
  "Action": ["kms:Decrypt", "kms:GenerateDataKey"],
  "Resource": "arn:aws:kms:eu-west-1:111122223333:key/1234abcd-12ab-34cd-56ef-1234567890ab",
  "Condition": {
    "StringEquals": { "kms:ViaService": "s3.eu-west-1.amazonaws.com" }
  }
}

El mínim és kms:GenerateDataKey; s'hi inclou kms:Decrypt perquè la funció també llegeix l'original. No cal kms:Encrypt: amb el xifratge de sobre, S3 no la fa servir mai.

(d) Sospitaria de la condició kms:ViaService. Si la funció hagués passat a cridar KMS directament —per exemple perquè algú va canviar el codi per xifrar abans de pujar, en lloc de deixar que ho faci S3—, la petició ja no arribaria «a través d'S3», la condició no es compliria i la política de clau la rebutjaria. També podria ser el Deny d'aws:RequestedRegion si el client de boto3 s'hagués creat apuntant a una altra regió. En tots dos casos l'arranjament no és ampliar permisos, sinó corregir com s'està cridant.

Conclusió

alias/mercadofresco-datos ha deixat de ser un nom. Ara és una clau gestionada pel client, amb política pròpia, rotació anual activada, etiquetes de projecte i una separació de funcions explícita: la Marta l'administra però no pot desxifrar; rol-mercadofresco-tienda i rol-lambda-miniaturas la fan servir però només a través d'S3, gràcies a kms:ViaService; RDS i EBS poden crear concessions perquè són serveis d'AWS; i ningú, ni el root, no la pot fer servir fora d'eu-west-1.

Entens el mecanisme que ho sosté tot, el xifratge de sobre: KMS genera una clau de dades i la retorna en dos formats —en clar i xifrada amb la mestra—, l'aplicació xifra localment a velocitat de CPU, esborra la versió en clar i desa el CiphertextBlob al costat de les dades. Per això les dades grans no viatgen mai a KMS, per això el límit de 4 KB d'Encrypt no és un problema real, i per això Decrypt no necessita que li diguis quina clau ha de fer servir. Saps també què fa i què no fa la rotació automàtica: canvia el material per al que és nou, conserva l'antic per sempre i no torna a xifrar el que ja està desat.

Distingeixes SSE-S3, SSE-KMS i SSE-C, i saps que l'avantatge real de SSE-KMS és la doble autoritzaciós3:GetObject i kms:Decrypt—, que converteix una exposició accidental en un ensurt en lloc d'una bretxa. Saps que les claus de bucket d'S3 retallen fins a un 99 % les crides a KMS a canvi de perdre la traça per objecte, i has decidit activar-les a mercadofresco-catalogo-fotos i no a mercadofresco-copias-basedatos, on la traça val més que l'estalvi.

En el pla pràctic, MercadoFresco ha xifrat el bucket de còpies amb una política de bucket que rebutja qualsevol pujada sense KMS, ha activat el xifratge per defecte d'EBS a eu-west-1, sap convertir un volum sense xifrar mitjançant copy-snapshot --encrypted —l'única operació que fa aquest salt— i ha migrat mercadofresco-pedidos a una instància xifrada pel camí de snapshot, còpia xifrada i restauració, sense oblidar que la restauració no conserva el grup de paràmetres ni Multi-AZ i que el punt d'enllaç canvia. Tot això per 1,00 USD al mes, la millor relació cost-benefici del curs. I saps que eliminar una clau és irreversible, que el camí assenyat és deshabilitar-la primer i que el període d'espera de 7 a 30 dies existeix precisament per salvar-te.

Queda, però, el forat més incòmode de tots, i és dels que no s'arreglen amb criptografia. La contrasenya de l'usuari mfadmin de mercadofresco-pedidos continua sent on no ha de ser: va passar pel user data de la plantilla lt-mercadofresco-tienda, viu en un fitxer de configuració del servidor i hi ha una còpia al .env que el Luis va compartir per xat quan va muntar l'entorn de desenvolupament. Tant li fa que la base de dades estigui xifrada amb AES-256: qualsevol que llegeixi aquell fitxer entra amb credencials vàlides, i el xifratge, com vam veure a la primera taula d'aquesta lliçó, no protegeix d'això. A la lliçó 04-03, «Secrets Manager i Parameter Store», aquella contrasenya sortirà per fi d'on és: veurem la diferència entre configuració i secret, el Parameter Store per al primer i el Secrets Manager per al segon, com es consumeix des de l'aplicació sense tornar-la a escriure mai en un fitxer, i sobretot la rotació automàtica cada 30 dies amb una Lambda gestionada, amb el seu cicle createSecret / setSecret / testSecret / finishSecret —que s'ancora, precisament, en la clau que acabes de construir.

Curs d'AWS

Mòdul 1: Introducció a AWS

Mòdul 2: Serveis principals d'AWS

Mòdul 3: Xarxes i lliurament de contingut

Mòdul 4: Seguretat i identitat

Mòdul 5: Monitoratge i gestió

Mòdul 6: Bases de dades

Mòdul 7: Integració d'aplicacions

Mòdul 8: Eines per a desenvolupadors

Mòdul 9: Infraestructura com a codi i govern de comptes

Mòdul 10: Contenidors a AWS

Mòdul 11: Millors pràctiques i gestió de costos

© Copyright 2026. Tots els drets reservats