Safeguard
Cloud Security

The AWS migration security checklist: IAM, encryption, and network segmentation

A misconfigured WAF and an over-permissioned IAM role exposed 106 million records in 2019 — here's the checklist that prevents a repeat during your AWS migration.

Safeguard Research Team
Research
6 min read

On July 29, 2019, Capital One disclosed that an attacker had accessed roughly 106 million credit-card applications and accounts by exploiting a misconfigured web application firewall running on EC2. The WAF's Server-Side Request Forgery flaw let the attacker query the EC2 instance metadata service and retrieve temporary credentials for an IAM role that was authorized to list and read more than 700 S3 buckets — far more than that WAF process ever needed. The attacker, later identified as a former AWS employee, had been pulling data since March 2019 before a tip from a GitHub user led to disclosure four months later; Capital One ultimately paid an $80 million OCC penalty and a $190 million class-action settlement. No zero-day was involved — the root cause was excessive IAM permissions combined with an SSRF path into the metadata service, both squarely inside the customer's side of AWS's shared responsibility model. Teams moving workloads to AWS keep making smaller versions of the same three mistakes: broad IAM roles, inconsistent encryption, and flat networks with no segmentation. This checklist walks through what to fix, in what order, before cutover.

What does AWS actually secure, and what's left to you?

AWS's shared responsibility model draws a clean line: AWS secures "of the cloud" — the physical data centers, host hypervisors, and managed-service infrastructure — while the customer secures "in the cloud," which covers guest operating systems, application code, data classification, IAM configuration, and network controls (aws.amazon.com/compliance/shared-responsibility-model). For IaaS services like EC2, that customer-side list is long: patching the OS, configuring security groups, encrypting the EBS volumes, and scoping every IAM role attached to the instance. For managed services like S3 or RDS, AWS handles more of the stack, but bucket policies, public-access settings, and encryption toggles remain yours. Nearly every well-known AWS breach, Capital One included, traces back to a customer-side control that was left permissive rather than a failure in AWS's own infrastructure. A migration checklist exists precisely because the responsibility split doesn't shrink when you move workloads faster — if anything, a rushed lift-and-shift multiplies the number of roles, buckets, and security groups you have to get right on day one.

Why does IAM over-permissioning keep causing the same breach?

Because temporary credentials from an over-permissioned role are just as dangerous as a leaked root key, and IAM policies default toward "broad enough to not break anything" under migration deadlines. In the Capital One case, the compromised role's policy allowed s3:GetObject and s3:ListBucket scoped so widely that it reached over 700 buckets, most of which had nothing to do with the WAF's function (cloudskope.com, reporting drawn from court filings and Krebs on Security's original coverage). The fix isn't exotic: scope every role to the specific bucket ARNs, tables, or queues it needs, use AWS IAM Access Analyzer to flag unused permissions before go-live, and treat any role with wildcard resources ("Resource": "*") as a migration blocker, not a temporary shortcut. Just as important, harden the path an SSRF exploit would take to reach those credentials in the first place: Since March 2024, AWS has let accounts set IMDSv2 — which requires a session token rather than a plain GET request — as the default for all new EC2 launches, and new instance types released since mid-2024 support IMDSv2 only; but that account-level default has to be turned on, it isn't retroactive, and any instance still permitted to use IMDSv1 during a migration should be switched over explicitly rather than left on the legacy setting.

How should data be encrypted in transit and at rest during a migration?

Both S3 and RDS support encryption at rest by default configuration options — S3 with SSE-S3 or SSE-KMS, RDS with storage encryption enabled at instance creation — and both enforce TLS for connections in transit when configured to require it. The catch is that "supported" isn't "on": S3 buckets can still be created without default encryption enabled, and an RDS instance's storage encryption can only be turned on at creation time, not retrofitted onto an existing unencrypted instance without a snapshot-and-restore migration. During a lift-and-shift, that means encryption has to be a launch-time decision, not a post-migration cleanup task, because fixing it later on RDS specifically requires downtime. The checklist item is concrete: enable SSE-KMS with customer-managed keys on every new bucket, enforce aws:SecureTransport in bucket policies to reject plaintext HTTP, turn on RDS storage encryption before the first restore, and require TLS-only parameter groups on the database engine itself.

What does real network segmentation look like inside a VPC?

Real segmentation means an attacker who compromises one workload can't reach every other workload in the account, which requires more than a single flat VPC with permissive security groups. The baseline pattern is separate subnets per tier — public subnets only for load balancers and NAT gateways, private subnets for application servers, and isolated subnets with no route to the internet for databases — paired with security groups that reference other security groups by ID rather than open CIDR ranges, so a database only accepts connections from the specific app-tier security group, not from "10.0.0.0/16." Network ACLs add a stateless second layer at the subnet boundary, and VPC endpoints (PrivateLink or gateway endpoints for S3 and DynamoDB) let workloads reach AWS services without traversing the public internet at all. A common migration mistake is treating segmentation as an afterthought applied after lift-and-shift, when in practice it's far cheaper to design the subnet and security-group layout before the first EC2 instance or RDS cluster launches, since retrofitting segmentation onto live, interconnected workloads means untangling security group rules one dependency at a time.

How Safeguard helps

Safeguard's DSPM connects directly to S3 and RDS as part of cloud discovery, recording exactly the posture signals this checklist calls for: bucket policy and public-access settings, storage encryption status, and network accessibility for every store it finds — plus it flags "shadow stores" that exist in the account but never made it into your managed inventory, which is exactly the kind of gap a fast migration tends to create. On the IAM side, Safeguard's secrets scanning detects AWS Access Key IDs wherever they leak — source code, container layers, build logs, Git history — and verifies each one live with a low-privilege sts:GetCallerIdentity call, so you know within seconds whether a credential exposed during migration is actually exploitable, with a revoke-via-AWS-IAM step built into the remediation playbook. Neither replaces reading through every IAM policy or subnet route table by hand, but together they turn "did we get this right" from a manual audit into a continuously monitored answer.

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.