Series Navigation
→ Part 8: AWS Security Deep Dive
The Shared Responsibility Model
AWS SHARED RESPONSIBILITY:
AWS is responsible for: YOU are responsible for:
───────────────────────────── ─────────────────────────────
Physical data centre security Your data (encryption)
Hardware (servers, networking) Application security
Hypervisor security IAM (who has access to what)
Managed service patching (RDS, etc.) OS patching (EC2)
Network config (Security Groups)
Compliance requirements
IaaS (EC2): AWS owns hardware, YOU own everything above the hypervisor
PaaS (ECS): AWS owns OS, YOU own app and data
SaaS (Gmail): Provider owns almost everything, you own your data/config
The most common breach pattern: "AWS was not hacked. The customer misconfigured their resources."
IAM — Identity and Access Management
IAM is the most critical security control in AWS. Everything depends on getting it right.
IAM Entities
IAM Entities:
├── Users → humans or service accounts (avoid for services)
├── Groups → collections of users with shared permissions
├── Roles → assumed by services, EC2 instances, Lambda functions
└── Policies → JSON documents defining permissions
Principle of Least Privilege — In Practice
// ❌ Overly permissive — common in dev environments that reach prod
{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Action": "*",
"Resource": "*"
}]
}
// ✅ Least privilege — Lambda that reads from one S3 bucket
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "ReadFromSpecificBucket",
"Effect": "Allow",
"Action": ["s3:GetObject", "s3:ListBucket"],
"Resource": [
"arn:aws:s3:::my-app-data-bucket",
"arn:aws:s3:::my-app-data-bucket/*"
]
},
{
"Sid": "WriteToSpecificPrefix",
"Effect": "Allow",
"Action": ["s3:PutObject"],
"Resource": "arn:aws:s3:::my-app-data-bucket/processed/*"
}
]
}
IAM Policy Conditions — Fine-Grained Control
{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Action": "s3:*",
"Resource": "*",
"Condition": {
"Bool": {
"aws:SecureTransport": "true" // require HTTPS
},
"StringEquals": {
"aws:RequestedRegion": "us-east-1" // restrict to region
},
"IpAddress": {
"aws:SourceIp": ["10.0.0.0/8", "172.16.0.0/12"] // VPC only
},
"StringLike": {
"s3:prefix": "home/${aws:username}/*" // users can only access own prefix
}
}
}]
}
Service Roles — Never Use Access Keys in Code
# ❌ Hardcoded credentials — the most common cloud breach cause
import boto3
s3 = boto3.client('s3',
aws_access_key_id='AKIAIOSFODNN7EXAMPLE',
aws_secret_access_key='wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY'
)
# ❌ Credentials in environment variables — still leaks via process listing, logs
import os
s3 = boto3.client('s3',
aws_access_key_id=os.environ['AWS_ACCESS_KEY_ID'],
aws_secret_access_key=os.environ['AWS_SECRET_ACCESS_KEY']
)
# ✅ IAM Role — no credentials at all
# Attach an IAM role to EC2/Lambda/ECS task
# boto3 automatically uses instance metadata service (IMDS)
s3 = boto3.client('s3') # that's it — credentials from role
response = s3.get_object(Bucket='my-bucket', Key='my-file.txt')
# ECS Task Role definition (Terraform)
resource "aws_iam_role" "app_task_role" {
name = "app-task-role"
assume_role_policy = jsonencode({
Version = "2012-10-17"
Statement = [{
Action = "sts:AssumeRole"
Effect = "Allow"
Principal = { Service = "ecs-tasks.amazonaws.com" }
}]
})
}
resource "aws_iam_role_policy" "app_task_policy" {
name = "app-task-policy"
role = aws_iam_role.app_task_role.id
policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Effect = "Allow"
Action = ["s3:GetObject"]
Resource = "arn:aws:s3:::${var.app_bucket}/*"
},
{
Effect = "Allow"
Action = ["secretsmanager:GetSecretValue"]
Resource = "arn:aws:secretsmanager:us-east-1:${data.aws_caller_identity.current.account_id}:secret:app/*"
}
]
})
}
Top 10 Cloud Misconfigurations
# 1. Public S3 buckets
aws s3api get-bucket-acl --bucket your-bucket
aws s3api get-public-access-block --bucket your-bucket
# Fix: Block all public access at account level:
aws s3control put-public-access-block \
--account-id $(aws sts get-caller-identity --query Account --output text) \
--public-access-block-configuration \
BlockPublicAcls=true,IgnorePublicAcls=true,BlockPublicPolicy=true,RestrictPublicBuckets=true
# 2. Overly permissive Security Groups
aws ec2 describe-security-groups \
--filters Name=ip-permission.cidr,Values='0.0.0.0/0' \
--query 'SecurityGroups[?IpPermissions[?IpRanges[?CidrIp==`0.0.0.0/0`]]].GroupId'
# Fix: Never open 0.0.0.0/0 except ports 80/443 on load balancers
# 3. No MFA on root account
aws iam get-account-summary | grep MFAEnabled
# 4. Access keys for root account
aws iam get-account-summary | grep AccountAccessKeysPresent
# 5. CloudTrail not enabled
aws cloudtrail describe-trails
aws cloudtrail get-trail-status --name your-trail
# 6. RDS publicly accessible
aws rds describe-db-instances \
--query 'DBInstances[?PubliclyAccessible==`true`].DBInstanceIdentifier'
# 7. EBS snapshots public
aws ec2 describe-snapshots --owner-ids self \
--query 'Snapshots[?State==`completed`]' \
| python3 -c "import json,sys; snaps=json.load(sys.stdin); [print(s['SnapshotId']) for s in snaps if any(p.get('Group')=='all' for p in s.get('CreateVolumePermissions',[]))]"
# 8. ECR images public
aws ecr describe-repositories --query 'repositories[*].repositoryName'
# 9. Lambda environment variables with secrets (visible in console/API)
aws lambda get-function-configuration --function-name my-function \
--query 'Environment.Variables'
# 10. No encryption on S3/RDS/EBS
aws s3api get-bucket-encryption --bucket your-bucket
aws rds describe-db-instances --query 'DBInstances[?StorageEncrypted==`false`].DBInstanceIdentifier'
Cloud Security Posture Management (CSPM)
Automatically detect misconfigurations across your cloud:
# Scout Suite — multi-cloud CSPM (free)
pip install scoutsuite
scout aws --report-dir ./scout-report
scout azure --report-dir ./scout-report
scout gcp --report-dir ./scout-report
# Prowler — AWS CIS benchmark (free)
pip install prowler
prowler aws --compliance cis_level2_aws
prowler aws -c s3_bucket_public_access # specific check
# Checkov — IaC security scanner (covers Terraform, CloudFormation)
pip install checkov
checkov -d ./terraform/
checkov -f cloudformation.yaml
# CloudSploit — cloud account scanner
npm install -g @aquasecurity/cloudsploit
cloudsploit scan --config config.js
Network Security — VPC Architecture
Secure VPC Architecture:
Internet Gateway
│
▼
Public Subnet (10.0.1.0/24)
├── Application Load Balancer ← only 80/443 from internet
└── NAT Gateway ← for outbound traffic from private subnets
│
▼
Private Subnet (10.0.2.0/24)
├── ECS/EC2 (application servers)
└── Lambda functions
│
▼
Database Subnet (10.0.3.0/24)
├── RDS (no public access)
└── ElastiCache
Security Groups (stateful firewall):
ALB-SG: Inbound 80,443 from 0.0.0.0/0
App-SG: Inbound 8080 from ALB-SG only
DB-SG: Inbound 5432 from App-SG only
Network ACLs (stateless firewall — additional layer):
Block known malicious IP ranges
Block all traffic except required ports
IAM Access Analyzer
# Find resources shared outside your account
aws accessanalyzer list-analyzers
aws accessanalyzer create-analyzer \
--analyzer-name account-analyzer \
--type ACCOUNT
# List findings (external access)
aws accessanalyzer list-findings \
--analyzer-arn arn:aws:access-analyzer:us-east-1:123456789:analyzer/account-analyzer \
--filter '{"status":{"eq":["ACTIVE"]}}'
# Use IAM Access Analyzer to generate least-privilege policies
# from CloudTrail activity logs
aws iam generate-service-last-accessed-details --arn arn:aws:iam::123456789:role/MyRole
aws iam get-service-last-accessed-details --job-id JOB_ID
Interview Questions
Q: A developer committed AWS access keys to a public GitHub repository. What do you do?
- Immediately rotate/revoke the exposed keys — before finishing this sentence
- Check CloudTrail logs for any unauthorized access since commit time
- Determine what permissions the keys had and what data could have been accessed
- Notify security team, document the incident
- Check if automated secret scanning is in place (truffleHog, git-secrets)
- Implement pre-commit hooks to prevent future key commits
- Consider moving to IAM roles to eliminate long-term credentials entirely
Q: What is the difference between IAM policies and S3 bucket policies?
IAM policies are attached to identities (users, roles) and control what that identity can do across all AWS services. S3 bucket policies are attached to the S3 resource and control who can access that specific bucket. Both must allow for access to be granted. You can use either or both — bucket policies are useful when granting cross-account access or applying conditions to all operations on a specific bucket.
What's Next
In Part 8 we go deeper into AWS security — KMS key management, CloudTrail audit logging, GuardDuty threat detection, Security Hub, WAF, and building a full security monitoring stack.
Discussion
Loading...Leave a Comment
All comments are reviewed before appearing. No links please.