Domain 1 of 4 · Chapter 3 of 3

Data Security Controls

KMS key policies: cross-account grants, condition keys, ViaService restriction

The one rule that decides every KMS access question is this: the key policy is the authoritative grant, and IAM alone is never enough. A key's access is governed by its key policy (the resource policy on the key) working together with IAM identity policies, and once that ordering is clear you can decide who may use a key across accounts, lock a key to one service, and choose between a grant and a key-policy edit for temporary access.

Key policy basics: a default key policy[24] grants the AWS account root user full key access; from there, IAM policies can grant key actions to other principals (but only because the root grant exists). No contradiction: the account-root statement is a delegation switch. It hands granting over to IAM, and IAM grants work only while it stays in the key policy. To remove this, scope the key policy to specific principals; every IAM Allow then stops working.

Cross-account access pattern:

{
  "Sid": "AllowCrossAccount",
  "Effect": "Allow",
  "Principal": { "AWS": "arn:aws:iam::CONSUMER_ACCOUNT:root" },
  "Action": ["kms:Decrypt", "kms:DescribeKey", "kms:CreateGrant"],
  "Resource": "*"
}

In the Principal, :root is account-level syntax: the whole consumer account, not its root user (kms:CreateGrant lets it create grants: temporary permissions, covered below). The consumer account's role still needs kms:Decrypt in its identity policy: two-layer (resource + identity) authorization, like S3 cross-account access: the key policy is the first gate, the identity policy the second.

Condition keys worth memorising:

  • kms:ViaService[21]: restricts the key to be used only through a specific AWS service. E.g. "kms:ViaService": "s3.us-east-1.amazonaws.com" means this key can only encrypt/decrypt via S3 in us-east-1. Defends against credentials being misused to decrypt the key through unintended services.
  • kms:CallerAccount: restricts to a specific account. Combined with ViaService for fine-grained scoping.
  • aws:SourceArn: restricts to a specific resource (e.g. only requests from one specific Lambda function or RDS instance).
  • kms:EncryptionContext: requires a specific encryption-context dict to be passed; the same context must be supplied at decrypt time. Common in envelope encryption (KMS encrypts a per-object data key, which encrypts the data); Secrets Manager scopes rotation to one secret this way.

Grants vs key policy:

  • Key policies are static: edit them and you change permanent permissions.
  • Grants[20] are temporary, programmatic: created via kms:CreateGrant, can be revoked or expire. AWS services (e.g. RDS encryption, EBS encryption) create grants automatically when they need temporary access.

For short-lived workloads needing temporary key access, grants are the pattern. For permanent integration, key policy.

ViaService restriction example (a common SAA scenario, applying kms:ViaService from above):

{
  "Effect": "Allow",
  "Principal": { "AWS": "arn:aws:iam::ACCOUNT:role/AppRole" },
  "Action": ["kms:Encrypt", "kms:Decrypt"],
  "Resource": "*",
  "Condition": {
    "StringEquals": {
      "kms:ViaService": "s3.us-east-1.amazonaws.com",
      "kms:CallerAccount": "123456789012"
    }
  }
}

This role can encrypt/decrypt via this key, but only when the request comes through S3 in us-east-1 from account 123456789012. Misuse via SQS / Lambda / direct KMS API is denied.

consumer-account role calls kms:Decrypt first gate — key policy (owning account): allows the external account or principal? no allows second gate — identity policy (consumer account): allows kms:Decrypt on the key? no allows ALLOW both sides consented — two-layer authorization DENY the key policy is the first gate, the consumer's identity policy the second — IAM alone is never enough
Two-layer cross-account KMS authorization: the owning account's key policy and the consumer's IAM identity policy must both allow. Either gate alone denies.

S3 encryption options: SSE-S3 vs SSE-KMS vs SSE-C vs DSSE-KMS, when each applies

S3 supports four server-side encryption options (SSE-KMS in two flavours, AWS-managed or customer-managed key, so five variants below); they differ in who controls the key and whether key use is audited. Since January 2023, SSE-S3 is enabled by default[1] on every new bucket. The exam tests when to upgrade to a more controlled option.

SSE-S3 (AES-256 with AWS-managed keys):

  • AWS manages key creation, rotation, and storage entirely.
  • Free; no KMS API charges.
  • NO CloudTrail audit of individual encrypt/decrypt operations.
  • Use for: default for non-sensitive data, public marketing content.

SSE-KMS with AWS-managed key (aws/s3):

  • AWS-managed key per region; free.
  • Adds KMS API charges per encrypt/decrypt request.
  • CloudTrail logs every key use (full audit trail).
  • Key policy not editable; fine for general compliance, not for cross-account.
  • Use for: compliance audit needs without cross-account complexity.

SSE-KMS with customer-managed key:

  • You create the key; ~$1/key/month + per-request charges.
  • Full key policy control: cross-account access, conditions like kms:ViaService from the first section.
  • Auto rotation (yearly) opt-in.
  • Use for: multi-tenant isolation (key-per-tenant), cross-account encrypted access, regulatory compliance.

SSE-C (customer-provided keys)[3]:

  • Caller provides the encryption key in every PUT/GET request (HTTPS only).
  • AWS doesn't store the key; caller responsible for key management.
  • No KMS API costs (KMS is bypassed entirely).
  • No CloudTrail of key use.
  • Use for: rare scenarios where customer MUST hold the key entirely outside AWS.

DSSE-KMS (Dual-layer SSE-KMS):

  • Each object encrypted twice with two independent KMS calls.
  • ~2× KMS request cost.
  • Required for FedRAMP High workloads.
  • Use for: government / defense workloads with explicit dual-layer requirement.

Decision pattern:

  • 'Default encryption / public marketing content' → SSE-S3 (auto-on).
  • 'Compliance audit / who decrypted what' → SSE-KMS (any flavour).
  • 'Cross-account encrypted access' → SSE-KMS with customer-managed key.
  • 'Multi-tenant data isolation' → SSE-KMS with separate customer-managed key per tenant.
  • 'FedRAMP High / dual-layer' → DSSE-KMS.
  • 'Customer must hold the key' → SSE-C.

Bucket-level enforcement: combine with a bucket policy denying non-encrypted uploads:

{
  "Effect": "Deny",
  "Principal": "*",
  "Action": "s3:PutObject",
  "Resource": "arn:aws:s3:::our-bucket/*",
  "Condition": {
    "StringNotEquals": { "s3:x-amz-server-side-encryption": "aws:kms" }
  }
}

This denies any PUT that doesn't explicitly request SSE-KMS. The January 2023 default already rules out plaintext. This policy guarantees nothing lands with weaker encryption than SSE-KMS.

what the scenario demands encryption option default / public marketing content SSE-S3 (auto-on) compliance audit / who decrypted what SSE-KMS (any flavour) cross-account access / multi-tenant isolation (key-per-tenant) SSE-KMS with customer-managed key FedRAMP High / dual-layer DSSE-KMS customer must hold the key SSE-C
The decision pattern in one view: match the scenario's demand to an S3 server-side encryption option.

ACM certificate provisioning: DNS vs email validation, auto-renewal lifecycle

AWS Certificate Manager (ACM)[5] issues free TLS/SSL certificates for AWS services. The exam tests when ACM works, where the certificate must live, and how validation + renewal happen.

Where ACM-issued certificates can be used:

  • CloudFront: certificate MUST be in us-east-1 (regardless of where your origin is).
  • ALB / NLB / API Gateway / App Runner: certificate must be in the SAME region as the resource.
  • Route 53: not a certificate consumer itself; it's where the DNS-validation records for ACM-issued certs live, in the hosted zone.

Issuance and validation are a short ordered flow whose one branch — the validation method you pick at step 2 — decides whether renewal is later hands-off or manual (the lifecycle diagram below traces exactly that branch):

  1. Request certificate: list domain names (FQDN + optional wildcards like *.example.com).
  2. Pick validation method:
    • DNS validation (preferred): ACM gives you a CNAME record to add to your domain's DNS. Once propagated, ACM auto-validates and issues. Renewal stays hands-off as long as the CNAME stays in place (see Auto-renewal below).
    • Email validation: ACM sends approval emails to standard addresses (admin@, hostmaster@, webmaster@) for the domain. Requires a manual click to approve, and again at every renewal (see below). Don't use unless you have to.
  3. Certificate issued → install on the AWS resource.

Auto-renewal:

  • DNS-validated certificates renew automatically[25] 60 days before expiry. ACM checks the CNAME, validates, reissues. Zero downtime if the certificate is attached to a managed AWS resource (CloudFront, ALB).
  • Email-validated certificates DON'T auto-renew; you must respond to a new email approval every year. Exam common trap: 'why did my ALB stop accepting HTTPS?' → email validation expired.

Private CA (AWS Private CA[26], formerly ACM Private CA):

  • AWS Private CA provides a managed private CA for internal certificates (services that talk to each other internally over TLS).
  • ~$400/month per CA[27] in general-purpose mode ($50/month in short-lived-certificate mode), billed per CA, so a root + subordinate hierarchy pays for each CA, plus per-certificate fees.
  • Used for mutual-TLS (mTLS) on internal services.

Importing third-party certificates:

  • You can import certs from external CAs (DigiCert, Let's Encrypt, etc.). Stored in ACM, usable on AWS services.
  • Imported certs DON'T auto-renew; you must re-import the renewed cert. ACM sends expiry notifications via CloudWatch.

Public vs private certificates:

  • ACM public certs (from Amazon Trust Services) are trusted by browsers worldwide. Free.
  • ACM private certs (issued from your AWS Private CA) are trusted only by clients that trust the private CA. For internal mTLS.

Exam patterns:

  • 'CloudFront with custom domain' → ACM cert in us-east-1.
  • 'ALB HTTPS in us-west-2' → ACM cert in us-west-2.
  • 'Internal microservices mTLS' → AWS Private CA + per-service private certs.
  • 'Imported cert from Let's Encrypt' → no auto-renewal; CloudWatch alarm on expiry.
1. Request certificate 2. Pick validation method DNS (preferred) email CNAME added — ACM auto-validates manual approval click required 3. Issued → install on resource 3. Issued → install on resource auto-renews 60 days before expiry hands-off while the CNAME stays in place manual re-approval every year miss it → HTTPS stops working the validation method picked at step 2 decides whether renewal is hands-off or manual
ACM lifecycle: the step-2 validation method sets the renewal fate — DNS auto-renews 60 days out, email needs a manual re-approval each year.

Block Public Access semantics: account vs bucket vs object level overrides

S3 Block Public Access[11] (BPA) is the strongest defense against accidental public bucket exposure. It applies at three levels (organization, account, bucket) with an override hierarchy: most restrictive wins (object ACLs sit beneath all three).

Four settings, each toggle-able:

  1. BlockPublicAcls: prevents new public ACLs from being applied. Existing public ACLs remain (unless setting #2 is also on).
  2. IgnorePublicAcls: ignores all public ACLs (existing or new). The bucket / object isn't removed; ACLs are just not honored.
  3. BlockPublicPolicy: prevents new bucket policies that grant public access.
  4. RestrictPublicBuckets: even if a bucket policy somehow grants public access, only AWS principals + AWS services can access; anonymous internet requests are blocked.

Level hierarchy (most restrictive wins):

  1. Organization level (S3 Block Public Access at the Organizations level): applies to every member account. Cannot be overridden by member account admins.
  2. Account level: per-account setting. Overrides bucket-level if more restrictive.
  3. Bucket level: per-bucket setting. Applies to that bucket only.
  4. Object level: individual object ACLs (still subject to all the above).

Trap the exam loves: a bucket policy that allows s3:GetObject from "Principal": "*" and BPA is on at the account level. Outcome: BPA wins; bucket is not public. The policy is evaluated but the public-access result is suppressed by BPA. This is correct: BPA was designed exactly to override misconfigurations.

Organization-wide enforcement pattern:

To enforce 'no S3 bucket can ever be public anywhere in our Organization':

  1. AWS Organizations → S3 BPA at root level → all four settings enabled.
  2. Optional SCP at root denying s3:PutPublicAccessBlock so member account admins can't disable BPA.
  3. AWS Config rule s3-bucket-public-access-prohibited to detect any drift.

When to disable BPA: only for intentional public static-website hosting. Even then, prefer CloudFront in front of a private bucket (with Origin Access Control) to expose content while keeping the bucket itself private.

SCPs vs BPA: SCPs can deny S3 actions (PutBucketPolicy, PutObjectAcl) but cannot inspect bucket policy content. BPA inspects content: it's the only way to enforce 'no public exposure' regardless of how a bucket policy is written. The exam answer for organizational guarantee is BPA at the org level, not SCP. Hence step 1 above is BPA; the SCP merely protects the setting.

Organization level every member account — admins below cannot override Account level overrides bucket level if more restrictive Bucket level that bucket only Object level object ACLs — subject to all the above most restrictive wins, top-down
Block Public Access override hierarchy: organization over account over bucket over object. The most restrictive level wins; lower levels can't undo higher ones.

Macie job tuning: managed identifiers vs custom regex, cost-control patterns

Amazon Macie[14] scans S3 buckets for PII, PHI, financial data, and credentials. It's S3-only. No other data sources. Cost is per-GB scanned + per-object analyzed; petabyte-scale buckets are expensive to scan fully.

Two finding types:

  1. Policy findings — public buckets, unencrypted buckets, ACL grants to anonymous. Discovered continuously, free.
  2. Sensitive data findings — actual PII detected in objects. Requires running a discovery job; costs per GB.

Managed identifiers (built-in regex + dictionary patterns):

  • PII: Social Security numbers (US), passport numbers (many countries), driver's licenses, credit card numbers, IBANs, bank account numbers.
  • PHI: medical record numbers, NPI, diagnosis codes.
  • Credentials: AWS access keys, OAuth tokens, SSH keys, X.509 private keys.
  • Country-specific: tax IDs, national IDs for ~30+ countries.

Managed identifiers are accurate (high precision, low false-positive rate). Use them by default.

Custom data identifiers — your own regex + keywords:

Regex: ^[A-Z]{2}\d{6}$  (e.g. employee ID format)
Keywords: ["employee", "emp_id"]
Maximum match distance: 50 characters
Ignore words: ["test", "sample"]

Use for: company-specific identifiers (employee IDs, internal codes), regulated data formats unique to your domain.

Cost-control patterns:

  1. Bucket inclusions / exclusions: scope a job to specific buckets only. Don't scan obvious-non-PII buckets (build artifacts, log archives older than 90 days).
  2. Object filtering: include only specific file extensions / prefixes / tags. E.g. scan only s3://uploads/ prefix where users put data, not s3://thumbnails/.
  3. Sampling depth: Macie samples a percentage of objects rather than scanning every one. Set sampling depth to balance cost vs coverage.
  4. One-time vs scheduled jobs: full one-time scan after migration; then monthly incremental scans on critical buckets.
  5. Findings aggregation: Macie publishes findings directly to Security Hub[28] (native integration) and to EventBridge as a separate destination. Security Hub for cross-account visibility, EventBridge for automated response.

Multi-account deployment:

  • Enable Macie at the Organizations level via the delegated administrator account.
  • Designate one account (typically security tooling account) as the Macie administrator; it gets visibility into all member accounts' findings.

Common exam scenario:

'After migration, audit S3 buckets across all accounts for PII without disrupting workloads' → Macie via Organizations + delegated administrator + one-time discovery job + scope to migration buckets + custom data identifier for company-specific employee IDs.

CloudHSM cluster setup: cross-AZ, client SDK, key replication

AWS CloudHSM[8] provides dedicated, FIPS 140-2 Level 3 validated Hardware Security Modules in your VPC. You control the keys entirely; AWS provides the hardware. Decision frame up front: KMS is the default. CloudHSM only when the requirement names dedicated hardware or full key custody: 'must store keys in a dedicated HSM', FedRAMP Moderate/High with HSM mandate, or government/defense.

Cluster topology:

A CloudHSM cluster = 1+ HSM instances in your VPC. Each HSM is ~$1.45/hr = ~$1 000/month. For production HA, run 2+ HSMs in different AZs (otherwise AZ failure = key loss).

Keys are replicated synchronously across HSMs in the same cluster. Operations are load-balanced.

Setup steps (mechanical, but the exam may test the sequence — the diagram below walks the same five steps in order):

  1. Create cluster — choose VPC + 2+ subnets in different AZs.
  2. Initialize cluster — generate the cluster's CA certificate; sign it.
  3. Activate HSMs — set the Crypto Officer (CO) credentials. CO administrates the HSM; can't perform crypto operations (separation of duties).
  4. Create Crypto Users (CU) — application identities that do encrypt/decrypt/sign/verify. Each CU has its own keys.
  5. Connect clients — install the CloudHSM Client SDK on EC2 instances, configure cluster endpoint, authenticate as a CU.

Client SDK options:

  • PKCS #11 — standard cryptographic API; works with most languages.
  • JCE (Java Cryptography Extension) — Java apps.
  • CNG (Cryptography Next Generation) — Windows / .NET.
  • OpenSSL Dynamic Engine — for apps using OpenSSL APIs.

Common integration patterns:

  1. TLS offload: nginx/Apache uses CloudHSM via OpenSSL engine to sign TLS handshakes. Private key never leaves the HSM.
  2. Code signing: build pipeline signs releases with a CloudHSM-stored signing key. Compliance-required for signed software.
  3. Database encryption: Oracle TDE / SQL Server TDE encrypts at the database engine layer using CloudHSM-stored keys.
  4. Custom AWS KMS Custom Key Store: bridge CloudHSM + KMS — KMS API on top of HSM-stored keys. Lets AWS services (S3, EBS) use HSM-backed keys transparently while you retain key custody.

Backup:

CloudHSM auto-backs up the cluster to S3 (encrypted, AWS-managed). Backups are tied to your cluster's CA certificate — restoring requires the original CA.

vs KMS:

  • KMS = multi-tenant HSMs under the hood; AWS controls the hardware. Its HSMs are validated at FIPS 140-3 Security Level 3[29] (Level 3 since May 2023) — the FIPS level no longer separates the two services. Cheaper, simpler.
  • CloudHSM = single-tenant hardware in YOUR VPC; you control the keys entirely. Required only when the spec explicitly mandates dedicated hardware or HSM key custody.

Exam triggers for CloudHSM:

  • 'Dedicated hardware' / 'single-tenant HSM' → CloudHSM.
  • 'Customer must control key material entirely' (full key custody) → CloudHSM (or imported key material in KMS, depending on context).
  • A bare 'FIPS 140-2 Level 3' mention is not a CloudHSM cue on its own — KMS HSMs are Level 3 validated too; look for single-tenancy or key custody in the scenario.
  • Otherwise → KMS is the default.
1. Create cluster VPC + 2+ subnets in different AZs 2. Initialize cluster generate + sign the cluster CA certificate 3. Activate HSMs set Crypto Officer (CO) credentials 4. Create Crypto Users (CU) identities that encrypt / decrypt / sign / verify 5. Connect clients install Client SDK, authenticate as a CU CO administrates but cannot perform crypto — separation of duties from the CU
CloudHSM setup, in order: create the cluster across AZs, initialize and sign its CA, activate HSMs with a Crypto Officer, create Crypto Users, then connect clients.

S3 server-side encryption options

TypeKey custodyPer-request costAuditable in CloudTrailUse when
SSE-S3AWS-managedNoNoDefault; non-sensitive data
SSE-KMS (AWS-managed key)AWS-managedYes (KMS request)YesCompliance audit needs
SSE-KMS (customer-managed key)You manage policy + rotationYesYesMulti-tenant or fine-grained access
SSE-CYou provide key per requestNo KMS costNoRare; specific compliance needs
DSSE-KMSAWS + customerYes (2x KMS)YesDefense-in-depth dual layer (FedRAMP High)

Decision tree

FIPS 140-2 Level 3 / dedicated HSM? Yes CloudHSM (hourly per HSM, cluster across AZs) No Need key policy / rotation control? No AWS-managed (free, opaque key policy) Yes Customer-managed KMS ($1/key/mo + per-request, full audit + cross-account) Multi-region replication? Yes Multi-Region KMS Key (same key ID region-to-region) No Single-region CMK chosen key FedRAMP High? No Keep chosen key option Yes DSSE-KMS (2x KMS cost, dual layer, FedRAMP High) decision chosen key type dedicated hardware

Sharp facts the exam loves — give these one last read before exam day.

Cheat sheet

Sharp facts the exam loves — scan these before test day.

Encrypt compliance data both at rest and in transit

Compliance data needs protection on both legs of its life (audits like HIPAA, PCI, and GDPR require it), so encrypt at rest with SSE/KMS and in transit with TLS, because neither covers the other. S3 already applies SSE-S3 by default since Jan 2023, so the at-rest leg is mostly handled, but in transit is on you: enforce it with a bucket policy that denies any request where aws:SecureTransport is false, forcing every connection onto TLS.

Trap Assuming S3's default SSE-S3 also protects data in transit: at-rest encryption says nothing about TLS; enforce the transit leg separately by denying aws:SecureTransport=false.

3 questions test this
Pick a KMS key type by control and cost, lowest first

Pick the cheapest KMS key tier that still meets your control requirement, climbing only as far as you need. AWS-owned keys are free but give no visibility; AWS-managed keys are free and add a CloudTrail audit trail; customer-managed keys cost ~$1/key/month plus per-request but unlock your own key policy, cross-account sharing, and rotation control; and CloudHSM is the most expensive, giving a dedicated single-tenant FIPS 140-2 Level 3 module. The control you require pushes you up a tier, not a default reflex.

Trap Reaching for CloudHSM for routine encryption: it's only worth it when you genuinely need single-tenant FIPS 140-2 Level 3, otherwise it just costs more than a customer-managed key.

5 questions test this
Control S3 access at the bucket level, not per object

Govern an S3 bucket with account- and bucket-level controls (Block Public Access plus default encryption) so protection applies uniformly and can't be undone object by object. Bucket-wide controls are the right altitude because they're auditable and don't drift, whereas per-object ACLs and policies multiply with object count, so they don't scale and are painful to verify across a large bucket.

Trap Securing a bucket with per-object ACLs: they're easy to misconfigure and impossible to audit at scale, which is exactly why Block Public Access exists to override them.

3 questions test this
Use Macie to find and classify sensitive data in S3

Macie is the managed service for discovering and classifying sensitive data (PII, PHI, financial data, credentials) in S3, using AWS-managed identifiers plus your own custom regex and routing findings through EventBridge for auto-remediation. It works on data already stored, scanning asynchronously, and runs ~$1/GB scanned, which is why you point it at the buckets that matter rather than sweeping the whole org blindly.

Trap Expecting Macie to be a real-time inline guard on uploads: it's an asynchronous discovery and classification job over data at rest, not an upload-path filter.

12 questions test this
New S3 buckets have been SSE-S3 encrypted by default since Jan 2023

Every new S3 bucket gets SSE-S3 automatically[1] since January 2023 with no action from you, and you can upgrade a bucket to SSE-KMS whenever you want tighter key control. The change is forward-looking, so buckets and objects created before 2023 may still be unencrypted: find them with the AWS Config rule s3-bucket-server-side-encryption-enabled rather than assuming the default reached back in time.

Trap Assuming the Jan-2023 default went back and encrypted old objects: it only applies to new buckets and objects, so legacy data stays unencrypted until you audit it.

KMS automatic rotation is yearly and opt-in for customer-managed keys

Customer-managed keys rotate the underlying key material automatically once a year[18] (the 365-day default, configurable to a shorter custom period via RotationPeriodInDays) but only after you opt in with the EnableKeyRotation API. It's off until you ask for it. AWS-managed keys already rotate yearly with nothing to configure. The exception is imported key material: KMS can't regenerate material it didn't create, so it never auto-rotates and you rotate it by re-importing.

Trap Turning on EnableKeyRotation and assuming imported key material rotates too. It's excluded, so it stays static until you manually re-import.

2 questions test this
KMS deletion is never instant: a 7–30 day window you can cancel

Deleting a KMS key is never instant: ScheduleKeyDeletion[19] starts a mandatory 7–30 day waiting period, and you can CancelKeyDeletion anytime inside it. The delay is deliberate: destroying a key makes everything it encrypted permanently unreadable, so the window is your chance to catch a mistake before it's irreversible. CloudHSM is the contrast: it lets you destroy a key immediately because you own the HSM.

Trap Counting on a deleted KMS key being recoverable like a soft-deleted resource: once the window elapses it's gone for good, so CancelKeyDeletion during the window is your only safety net.

Macie cost scales with data, so scan once then target critical buckets

Macie charges ~$1/GB scanned plus per-object analysis, so the bill tracks how much data you point it at: steep on petabyte-scale buckets, and rescanning unchanged data adds cost without adding signal. The cost-aware pattern is one full discovery pass after a migration, then scheduled monthly scans limited to critical buckets, using excludes[15] filters to skip large prefixes you already know are non-sensitive.

Trap Running continuous org-wide Macie discovery to be thorough: rescanning the same data over and over just multiplies the per-GB cost for no new signal, so scope and schedule it instead.

3 questions test this
Give a short-lived workload a key with a KMS grant, not a policy edit

When a short-lived workload needs key access, issue a grant[20], a temporary, programmatic permission you create with kms:CreateGrant and retire when done, because a grant is scoped to specific operations and goes away cleanly. Editing the key policy is permanent and shared: it changes who can use the key until someone edits it back, so it's wrong for ephemeral consumers. This is exactly the mechanism AWS services like RDS encryption use, creating grants for you behind the scenes.

Trap Editing the key policy for every ephemeral consumer: it permanently bloats the policy and has to be revoked by hand, whereas a grant is scoped and retirable for exactly this case.

3 questions test this
Lock a KMS key to one service with the kms:ViaService condition

The kms:ViaService[21] condition in a key policy (e.g. Condition: { StringEquals: { kms:ViaService: "s3.us-east-1.amazonaws.com" } }) restricts a key so it can be used only when the request comes through that one service in that one Region. It checks which service is making the call on a principal's behalf, defending against stolen credentials being replayed against the key through some other service: a containment control that complements, not replaces, the permissions on the principal itself.

Trap Assuming kms:ViaService restricts which principal can use the key: it constrains the calling service and Region, not the identity, so it pairs with principal-level permissions rather than replacing them.

4 questions test this
Use S3 Object Lock for immutable retention. Compliance mode blocks even root

S3 Object Lock provides WORM (write-once-read-many) immutability in two modes: Governance, where a privileged admin can still override the lock, and Compliance, where nobody (not even the root user) can shorten retention or delete during the window. Compliance satisfies records rules like SEC 17a-4(f), FINRA, and CFTC[22] precisely because no one can tamper with it. You apply it per object or as a bucket default retention policy, and versioning must be enabled first.

Trap Using Governance mode for a regulatory WORM requirement: a privileged admin can override it, so only Compliance mode satisfies SEC 17a-4(f)-style immutability.

2 questions test this
An ACM cert for CloudFront has to live in us-east-1

An ACM certificate attached to CloudFront must be requested or imported in US East (N. Virginia) / us-east-1, regardless of where your origin or users sit, because CloudFront is a global service that distributes that single us-east-1 certificate out to every edge location. A cert created in any other Region won't appear in the CloudFront console for you to select.

Trap Requesting the ACM cert in the origin's Region where your ALB or S3 bucket lives: CloudFront only sees certificates in us-east-1, so it'll never appear for you to attach.

4 questions test this
For cross-account S3 uploads, set Object Ownership to Bucket owner enforced

Setting S3 Object Ownership to Bucket owner enforced makes the bucket owner automatically own every object (even ones uploaded from another account) while ACLs are disabled entirely, so access is governed purely by bucket and IAM policies. That's the clean fix for cross-account uploads, where by default the uploading account retains ownership of its objects and leaves the bucket owner unable to read them. It's now the recommended default for new buckets for exactly this reason.

Trap Granting the bucket owner an ACL on each cross-account object instead: that's brittle per-object work, whereas Bucket owner enforced transfers ownership automatically and removes ACLs entirely.

10 questions test this
Org-level S3 Block Public Access overrides account and bucket settings

S3 Block Public Access enforced at the AWS Organizations root or OU propagates to every member account, including ones that join later, and overrides account- and bucket-level settings so a local admin can't switch it off. That central enforcement removes accidental public exposure as an option across the whole org. If one account legitimately needs a public bucket, the carve-out is made at the org by excluding that account, not by a local toggle.

Trap Telling a member-account admin to just toggle Block Public Access off locally: the org-level setting wins, so the carve-out has to happen at the organization, not the account.

7 questions test this
On high-traffic SSE-KMS buckets, enable S3 Bucket Keys to cut KMS costs ~99%

On a high-traffic SSE-KMS bucket, enable S3 Bucket Keys: KMS hands S3 a short-lived bucket-level key from which S3 derives the per-object data keys itself, collapsing many per-object KMS calls into far fewer, cutting KMS request charges by up to ~99%. There's no security trade-off, because every object is still encrypted under your customer managed key; you've only changed how often S3 has to call KMS.

Trap Switching from SSE-KMS to SSE-S3 just to dodge per-request KMS charges: that throws away the customer-managed-key control, whereas Bucket Keys cut the cost while keeping the CMK.

6 questions test this
Cross-account KMS access needs both a key policy and an IAM policy

Cross-account use of a customer managed KMS key takes consent from both sides: the key policy in the owning account must grant the external account or principal, and an IAM policy in the external account must allow those principals to use that specific key ARN. The key policy decides who may have access and the IAM policy decides who actually does. Both have to line up, so neither alone authorizes the call.

Trap Setting only the key policy as you would for same-account access. Cross-account principals also need an explicit IAM allow on the key ARN, or the call still fails.

8 questions test this
Changing default encryption doesn't touch existing objects: re-encrypt with S3 Batch Operations

Updating a bucket's default encryption is forward-looking only: it applies to newly uploaded objects while everything already stored keeps its original encryption, so flipping the setting alone never re-keys old data. To re-encrypt objects already in the bucket (even billions) under a new SSE-KMS customer managed key, run S3 Batch Operations with the Copy operation, copying objects back into the same bucket with the new encryption, which rewrites each object under the new key.

Trap Assuming flipping the bucket's default encryption re-encrypts what's already stored: it's forward-looking only, so old objects stay on the old key until you copy them.

4 questions test this
KMS keys are Regional, so cross-Region replicas need a key in the target Region

A KMS key is a Regional resource and can't be invoked from another Region, so any cross-Region encrypted copy needs its own key in the destination Region. An encrypted cross-Region RDS read replica must be given a customer managed (or AWS managed) key that exists in the target Region, and an S3 bucket can only use a same-Region KMS key for SSE-KMS. The encryption key always has to be local to the data.

Trap Pointing the destination Region at the source key's ARN: KMS won't resolve it across Regions, so the replica creation fails until a key exists locally.

4 questions test this
Run org-wide Macie from a delegated security account, not the management account

In AWS Organizations the management account designates a dedicated security account as the Macie delegated administrator, and that account is where you enable Macie across members, run org-wide discovery jobs, and aggregate findings centrally. Delegating this out of the management account keeps day-to-day security operations (and their blast radius) away from the most privileged account in the org. Automated sensitive data discovery then samples objects to score each bucket's sensitivity cost-efficiently.

Trap Operating Macie straight from the Organizations management account: AWS guidance is to delegate to a separate security account to limit management-account exposure and blast radius.

6 questions test this
For CloudFront HTTPS with a custom cert, use free SNI SSL, not Dedicated IP

For CloudFront HTTPS with your custom certificate, use SNI (Server Name Indication) SSL, which lets one IP present the right cert per hostname and carries no extra monthly charge. Every browser released after 2010 supports it, so it's the right default for essentially all modern traffic. Dedicated IP SSL exists only for the rare legacy client that can't do SNI, and it adds a per-distribution monthly fee, so you pick it for compatibility, never for robustness.

Trap Picking Dedicated IP SSL to look more robust: it just adds per-distribution monthly cost, whereas SNI is the default and works for every modern browser.

6 questions test this
To encrypt CloudFront-to-origin, set Origin Protocol Policy to HTTPS Only with min TLS 1.2

To encrypt the leg between CloudFront and a custom origin, set the Origin Protocol Policy to HTTPS Only with a minimum origin SSL protocol of TLSv1.2, forcing every origin fetch onto modern TLS. Once HTTPS Only is on, CloudFront validates the origin's certificate and returns HTTP 502 (Bad Gateway) if it's self-signed or otherwise untrusted, so the origin must present a certificate signed by a trusted CA for the connection to succeed.

Trap Leaving a self-signed cert on the origin under HTTPS Only and then blaming the 502 on CloudFront. CloudFront refuses untrusted origin certs, so it needs a CA-signed certificate.

3 questions test this
SSE-KMS needs kms:GenerateDataKey to upload and kms:Decrypt to download

SSE-KMS splits the two directions across two permissions: uploading makes S3 ask KMS to generate a data key, needing kms:GenerateDataKey, while downloading makes S3 decrypt that data key, needing kms:Decrypt. An app that both reads and writes must hold both, because a policy missing either one fails only the matching S3 operation with AccessDenied while the other keeps working, which is what makes the gap confusing to diagnose.

Trap Granting only kms:Decrypt just because the app reads objects: uploads will then fail with AccessDenied, since writing SSE-KMS objects requires kms:GenerateDataKey.

5 questions test this
Cross-account secret access needs a secret resource policy and the KMS key policy

Cross-account reads of a Secrets Manager secret require two grants together: a resource-based policy on the secret allowing secretsmanager:GetSecretValue, and a policy on the encrypting KMS key allowing kms:Decrypt, because retrieving the secret means decrypting it. This is why the default aws/secretsmanager key doesn't work cross-account: its key policy is immutable and can't be edited to admit the other account, so cross-account access forces you onto a customer managed key.

Trap Encrypting the secret with the default aws/secretsmanager key and expecting cross-account access: its key policy can't be edited to grant the other account, so you have to use a customer managed key.

4 questions test this
Scope a rotation Lambda's KMS decrypt to one secret with kms:EncryptionContext:SecretARN

A rotation Lambda for a secret encrypted with a customer managed key needs kms:Decrypt on that key, but granting it plainly lets it decrypt every secret sharing the key. Add a condition on kms:EncryptionContext:SecretARN and the decrypt permission is confined to the one secret it's meant to rotate: least privilege in action, since the encryption context is bound to the secret's ARN and KMS only allows the decrypt when that context matches.

Trap Granting the rotation function plain kms:Decrypt on the shared key: it can then decrypt every secret encrypted with that key, so the SecretARN encryption-context condition is what confines it.

Restrict what flows through a VPC endpoint with an endpoint policy

An Interface or Gateway endpoint can carry its own endpoint policy[23] that limits which API actions and resources may pass through it, for example an S3 Gateway endpoint scoped to permit s3:GetObject on your buckets only and deny everything else routed through it. It's a filter on the path, not a grant of access, so it's an extra constraint evaluated alongside the principal's IAM and the bucket policy rather than replacing them.

Trap Assuming an endpoint policy grants access on its own: it only filters what the endpoint permits, so the principal still needs IAM and bucket permissions, and the two are evaluated together.

Also tested in

References

  1. Amazon S3 default encryption now SSE-S3 (AWS News Blog, Jan 2023) Blog
  2. Specifying server-side encryption with AWS KMS (SSE-KMS)
  3. Protecting data using server-side encryption with customer-provided keys (SSE-C)
  4. Using dual-layer server-side encryption with AWS KMS keys (DSSE-KMS)
  5. AWS Certificate Manager User Guide
  6. AWS KMS concepts (key types)
  7. AWS KMS pricing
  8. AWS CloudHSM introduction
  9. Getting started with AWS CloudHSM
  10. AWS KMS multi-region keys overview
  11. Blocking public access to S3 storage
  12. Setting default server-side encryption for an S3 bucket
  13. Controlling ownership of objects (Object Ownership)
  14. What is Amazon Macie
  15. Macie sensitive-data discovery jobs
  16. What is AWS Security Hub
  17. Amazon Macie pricing
  18. Rotating AWS KMS keys
  19. Deleting AWS KMS keys
  20. Using grants in AWS KMS
  21. AWS KMS policy conditions (kms:ViaService)
  22. Locking S3 objects (Object Lock)
  23. https://docs.aws.amazon.com/vpc/latest/privatelink/vpc-endpoints-access.html
  24. https://docs.aws.amazon.com/kms/latest/developerguide/key-policy-default.html
  25. https://docs.aws.amazon.com/acm/latest/userguide/managed-renewal.html
  26. https://docs.aws.amazon.com/privateca/latest/userguide/PcaWelcome.html
  27. https://aws.amazon.com/private-ca/pricing/
  28. https://docs.aws.amazon.com/macie/latest/user/securityhub-integration.html
  29. https://aws.amazon.com/kms/faqs/