Domain 2: Security
Topic 2 of 4 · Study notes
AWS Certified Developer – Associate (DVA-C02)
Domain 2: Security
Exam Code: DVA-C02 | Level: Associate
Domain Weight: 26% | Total Domains: 4 | Passing Score: 720/1000
Table of Contents
- AWS IAM — Identity & Access Management
- AWS STS — Security Token Service
- AWS KMS — Key Management Service
- Amazon S3 Security
- Amazon Cognito Security
- API Gateway Security
- Secrets Manager & SSM Parameter Store
- Encryption at Rest & In Transit
- Network Security
- AWS CloudTrail & Audit
- Exam Tips & Quick Reference
1. AWS IAM — Identity & Access Management
IAM is the security centre of every AWS account. It controls who can authenticate (who you are) and what they are authorized to do (what you can access). Every AWS API call is authorized through IAM.
1.1 Core Components & Policy Structure
| Component | Description |
|---|---|
| Root Account | Created with the AWS account. Has unrestricted access. Never use for daily tasks. |
| Users | Individual identities for humans or applications. Have long-term credentials (passwords and access keys). |
| Groups | Collections of IAM users. Policies attached to a group apply to all its members. |
| Roles | Identities assumed temporarily. No long-term credentials. Used by services, applications, and federated users. |
| Policies | JSON documents that define permissions. Attached to users, groups, or roles. |
Key structural rules:
- Groups contain users only — you cannot nest groups inside groups.
- A user can belong to multiple groups simultaneously.
- Roles are assumed temporarily via STS — they issue short-lived credentials.
IAM Policy JSON Structure:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AllowS3Read",
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::123456789012:root"
},
"Action": ["s3:GetObject", "s3:ListBucket"],
"Resource": ["arn:aws:s3:::my-bucket/*"],
"Condition": {
"StringEquals": { "aws:RequestedRegion": "us-east-1" }
}
}
]
}
| Field | Required | Notes |
|---|---|---|
Version |
Yes | Always "2012-10-17". Omitting it causes subtle permission failures. |
Effect |
Yes | "Allow" or "Deny". |
Principal |
Resource policies only | Specifies who the policy applies to. Not used in identity-based policies. |
Action |
Yes | AWS API actions (e.g., s3:GetObject, ec2:*). |
Resource |
Yes | ARN of the target resource. Use "*" to match all resources. |
Condition |
No | Optional constraints — MFA required, source IP, time of day, etc. |
Critical: The
Principalfield only appears in resource-based policies (S3 bucket policies, SQS queue policies, Lambda resource policies, KMS key policies). Identity-based policies attached to users, groups, and roles do NOT include aPrincipal.
Policy Types:
| Type | Attached To | Key Characteristic |
|---|---|---|
| AWS Managed | Users, groups, roles | Maintained and updated by AWS. Suitable for common use cases. |
| Customer Managed | Users, groups, roles | You own them. Versioned, reusable, centrally managed. Best practice. |
| Inline | A single user, group, or role | Strict 1:1 binding. Deleted when the principal is deleted. Not reusable. |
| Resource-based | AWS resources (S3, SQS, Lambda) | Attached to the resource itself. Includes Principal. Enables cross-account access. |
1.2 Policy Evaluation Logic
┌─────────────────────────────────────────────────────────────────────┐
│ IAM Policy Evaluation Flow │
│ │
│ Start: Default DENY (implicit) │
│ │
│ Evaluate ALL applicable policies: │
│ • Identity-based policies (user, group, role) │
│ • Resource-based policies (S3 bucket policy, etc.) │
│ • Permissions boundaries │
│ • SCPs (Service Control Policies, if using AWS Organizations) │
│ │
│ ┌────────────────────────────────────────┐ │
│ │ Any EXPLICIT DENY found? │──► YES ──► Final: DENY │
│ └────────────────────────────────────────┘ │
│ │ NO │
│ ▼ │
│ ┌────────────────────────────────────────┐ │
│ │ Any EXPLICIT ALLOW found? │──► YES ──► Final: ALLOW │
│ └────────────────────────────────────────┘ │
│ │ NO │
│ ▼ │
│ Final: DENY (implicit) │
└─────────────────────────────────────────────────────────────────────┘
Critical: An explicit DENY always wins over any ALLOW, regardless of which policy contains it. There is no way to override an explicit DENY.
Union evaluation — IAM + S3 Bucket Policy:
| IAM Policy | S3 Bucket Policy | Result |
|---|---|---|
| Allow | No bucket policy | Allow — IAM policy alone is sufficient |
| Allow | Explicit Deny | Deny — explicit deny always wins |
| No S3 permissions | Allow | Allow — resource policy alone is sufficient |
| Explicit Deny | Allow | Deny — explicit deny always wins |
1.3 IAM Roles & iam:PassRole
Role components:
- Trust Policy — defines who can assume this role (the principal: a service, another account, a user).
- Permission Policy — defines what the role can do once assumed.
// Trust Policy example — allows EC2 to assume this role
{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Principal": { "Service": "ec2.amazonaws.com" },
"Action": "sts:AssumeRole"
}]
}
Common service role assignments:
| Service | Role Type | Purpose |
|---|---|---|
| EC2 | Instance Profile | Allows application code on EC2 to call AWS APIs without hardcoded keys |
| Lambda | Execution Role | Allows Lambda to access S3, DynamoDB, CloudWatch Logs, etc. |
| ECS Task | Task Role | Allows container application code to access AWS services |
| CodeBuild | Service Role | Allows CodeBuild to pull from CodeCommit, push to ECR, read Parameter Store |
| CloudFormation | Service Role | Allows CloudFormation to create, update, and delete stack resources |
iam:PassRole — The gatekeeper permission:
When you assign an IAM role to an AWS service (launch EC2 with a role, create a Lambda function with an execution role), you are "passing" that role to the service. AWS requires you to have explicit permission to do this.
// Policy granting a developer the ability to pass a specific role to EC2
{
"Effect": "Allow",
"Action": ["iam:PassRole", "iam:GetRole"],
"Resource": "arn:aws:iam::123456789012:role/EC2AppRole"
}
Critical Exam Trap: Having
ec2:RunInstancespermission does NOT grant the ability to launch an EC2 instance with an IAM role.iam:PassRoleis a separate, required permission. Missing it results in Access Denied even when the user has full EC2 permissions.
1.4 Permissions Boundary & Condition Keys
Permissions Boundary:
A managed policy that acts as an upper limit on what permissions an identity policy can grant. The effective permissions are the intersection of the boundary and the identity policy.
Permissions Boundary: Allow S3, EC2
Identity Policy: Allow S3, DynamoDB
────────────────────────────────────────
Effective Permissions: Allow S3 only
Key Rule: A boundary does not GRANT permissions.
It only LIMITS what identity policies can grant.
Use case: allow developers to create IAM roles for their services, but prevent them from creating roles with permissions beyond what they themselves have (preventing privilege escalation).
Common IAM Condition Keys:
// Require MFA for sensitive actions
"Condition": { "Bool": { "aws:MultiFactorAuthPresent": "true" } }
// Restrict access to a specific AWS region
"Condition": { "StringEquals": { "aws:RequestedRegion": "us-east-1" } }
// Require HTTPS (deny non-secure transport)
"Condition": { "Bool": { "aws:SecureTransport": "true" } }
// S3: restrict access to each user's own prefix
"Condition": { "StringLike": { "s3:prefix": ["home/${aws:username}/*"] } }
// DynamoDB: restrict access to each user's own rows
"Condition": {
"ForAllValues:StringEquals": {
"dynamodb:LeadingKeys": ["${aws:userid}"]
}
}
// Restrict by resource tag value
"Condition": { "StringEquals": { "aws:ResourceTag/Environment": "Production" } }
1.5 Credential Chain & Audit Tools
AWS CLI and SDK Credential Resolution Order (first match wins):
1. Command-line options (e.g., --profile, --region flags)
2. Environment variables (AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_SESSION_TOKEN)
3. AWS credentials file (~/.aws/credentials)
4. AWS config file (~/.aws/config)
5. ECS container credentials (for tasks running on ECS)
6. EC2 Instance Profile (IMDS at 169.254.169.254) ← lowest priority
Critical Exam Trap: If environment variables are set on an EC2 instance, they override the Instance Profile — even though the Instance Profile is the recommended and more secure approach. Always remove hardcoded environment variable credentials from EC2 and rely on the Instance Profile instead.
IAM Audit Tools:
| Tool | What It Shows |
|---|---|
| IAM Credentials Report | Account-level snapshot: all users, their password status, access key ages, last used dates, MFA status |
| IAM Access Advisor | Per user/role: which AWS services were last accessed and when. Use to identify and remove unused permissions. |
| AWS CloudTrail | All IAM API calls with caller identity, timestamp, and source IP |
IAM Best Practices:
- Never use the root account for daily operations. Lock it down and enable MFA.
- Enable MFA on all privileged users.
- One physical person = one IAM user. Never share credentials.
- Assign permissions to groups, not directly to individual users.
- Use IAM roles for all service-to-service access. Never hardcode access keys.
- Rotate access keys regularly. Use the Credentials Report to find old keys.
- Apply least privilege — grant only what is needed and nothing more.
2. AWS STS — Security Token Service
STS issues temporary, limited-privilege security credentials for IAM users, federated users, or AWS services assuming roles.
2.1 Key APIs & Cross-Account Access
| API | Use Case |
|---|---|
AssumeRole |
Assume an IAM role in the same account or a different account |
AssumeRoleWithSAML |
Federate with a corporate SAML 2.0 identity provider (Active Directory, Okta) |
AssumeRoleWithWebIdentity |
Federate with a web identity provider (Google, Facebook, OIDC). AWS recommends using Cognito instead. |
GetSessionToken |
Get temporary credentials that satisfy MFA-required IAM conditions |
GetCallerIdentity |
Returns the account ID, user ID, and ARN of the current caller. Essential for debugging identity issues. |
DecodeAuthorizationMessage |
Decodes the encoded error message returned in an authorization failure response |
Cross-Account Access Flow:
┌─────────────────────────────────────────────────────────────────────┐
│ Cross-Account Role Assumption │
│ │
│ Account A (Dev) Account B (Prod) │
│ ┌──────────────┐ ┌───────────────────────────┐ │
│ │ IAM User │──sts:AssumeRole──►│ IAM Role "ReadProdData" │ │
│ │ "Alice" │ │ Trust: Account A allowed │ │
│ └──────────────┘ └──────────────┬────────────┘ │
│ │ │ │
│ │◄─────── Temporary credentials ───────────┘ │
│ │ (AccessKey, SecretKey, SessionToken) │
│ │ │
│ └──────────────────────────────────────────────────────────► │
│ Access Account B resources using temp creds │
└─────────────────────────────────────────────────────────────────────┘
Setup requirements for cross-account access:
- Account B: Create an IAM Role with a Trust Policy that allows Account A's user or role to assume it.
- Account A: Attach an IAM Policy to the user/role that allows
sts:AssumeRoleon Account B's role ARN.
Temporary credential duration:
- Minimum: 15 minutes.
- Maximum: 1 hour for most role assumptions.
- Maximum: 12 hours for console federation sessions.
MFA with CLI:
# Step 1: Get temporary credentials using your MFA device
aws sts get-session-token \
--serial-number arn:aws:iam::123456789012:mfa/alice \
--token-code 123456 \
--duration-seconds 3600
# Step 2: Export the returned credentials as environment variables
export AWS_ACCESS_KEY_ID=ASIA...
export AWS_SECRET_ACCESS_KEY=...
export AWS_SESSION_TOKEN=...
# Now run commands that require MFA — credentials contain the MFA assertion
3. AWS KMS — Key Management Service
KMS is the central service for creating and managing cryptographic keys. All KMS API calls are logged in CloudTrail, providing a complete audit trail of key usage.
3.1 Key Types & Algorithms
By Ownership:
| Key Type | Monthly Cost | Auto-Rotation | Level of Control |
|---|---|---|---|
| AWS Owned | Free | AWS-managed | None — used internally by AWS services |
| AWS Managed | Free | Every 1 year (automatic) | View only — you cannot change policies |
| Customer Managed (CMK) | $1/month | Optional (enable for 1-year rotation) | Full control — policies, rotation, deletion |
| Imported Key Material | $1/month | Manual only — via alias update | You supply and manage the key material |
Critical: Imported key material does NOT support automatic rotation. To rotate, you must create a new CMK, import new material, and update the alias to point to the new key.
By Algorithm:
| Algorithm | Type | Use Case |
|---|---|---|
| AES-256 (Symmetric) | Single key for encrypt and decrypt | All AWS service integrations (S3, EBS, RDS). You never see the raw key material. |
| RSA / ECC (Asymmetric) | Public key + private key pair | Sign/verify or encrypt/decrypt. Download public key for use outside AWS without calling KMS. |
3.2 KMS APIs & Envelope Encryption
Core KMS APIs:
| API | Data Size Limit | Purpose |
|---|---|---|
Encrypt |
4 KB maximum | Encrypt small data (passwords, tokens) directly using a CMK |
Decrypt |
4 KB maximum | Decrypt data previously encrypted by KMS |
GenerateDataKey |
No limit | Generate a data encryption key — returns plaintext copy AND encrypted copy |
GenerateDataKeyWithoutPlaintext |
No limit | Returns only the encrypted copy — for deferred use |
ReEncrypt |
4 KB | Decrypt then re-encrypt under a different CMK — key material never leaves KMS |
Critical: The
EncryptAPI has a hard 4 KB limit. For anything larger — files, documents, database records — you must use Envelope Encryption viaGenerateDataKey.
Envelope Encryption:
┌─────────────────────────────────────────────────────────────────────┐
│ Envelope Encryption Flow │
│ │
│ ENCRYPTION: │
│ 1. Call GenerateDataKey (CMK ARN) │
│ → Returns: Plaintext DEK + Encrypted DEK │
│ 2. Use Plaintext DEK to encrypt your large file locally (AES-256) │
│ 3. Immediately discard Plaintext DEK from memory │
│ 4. Store: [ Encrypted File ] + [ Encrypted DEK ] together │
│ │
│ DECRYPTION: │
│ 1. Retrieve: [ Encrypted File ] + [ Encrypted DEK ] │
│ 2. Call KMS Decrypt with the Encrypted DEK │
│ → Returns: Plaintext DEK │
│ 3. Use Plaintext DEK to decrypt the file locally │
│ 4. Immediately discard Plaintext DEK from memory │
│ │
│ Key insight: The CMK never leaves KMS. Only the small Encrypted DEK │
│ travels with your data. KMS never sees your raw file content. │
└─────────────────────────────────────────────────────────────────────┘
Exam Rule: Any question about encrypting data larger than 4 KB using KMS → the answer involves
GenerateDataKeyand Envelope Encryption.
3.3 Key Policies, Quotas & CloudHSM
Key Policies:
- Every CMK must have a key policy. Without one, no one — not even the root account — can use the key.
- The default key policy grants the root account full control, allowing IAM policies to then delegate access.
- A custom key policy specifies exactly which principals can administer and use the key.
- Cross-account KMS access requires: (1) key policy allows the other account, AND (2) IAM policy in the other account allows the action.
KMS Quotas (causes ThrottlingException):
| Region Group | Symmetric CMK Request Quota |
|---|---|
| us-east-1, us-west-2, eu-west-1 | 30,000 requests/second |
| Most other regions | 5,500 – 10,000 requests/second |
When throttled: implement exponential backoff. For S3 workloads using SSE-KMS at high volume, enable S3 Bucket Keys to reduce KMS API calls by approximately 99%.
KMS vs CloudHSM:
| Feature | AWS KMS | AWS CloudHSM |
|---|---|---|
| Hardware tenancy | Multi-tenant (shared hardware) | Single-tenant (dedicated physical HSM) |
| Key control | AWS manages the hardware and software | You manage keys entirely |
| FIPS compliance | FIPS 140-2 Level 3 | FIPS 140-2 Level 3 |
| AWS service integration | Direct (native integration) | Via KMS Custom Key Store |
| Cost | $1/month per CMK | ~$1.60/hour per HSM |
| Choose when | Standard encryption needs | Regulatory compliance mandating dedicated hardware, full key ownership |
4. Amazon S3 Security
4.1 Access Control Layers
S3 has multiple overlapping access control mechanisms. Understanding how they interact — especially with Block Public Access — is critical for the exam.
| Mechanism | Scope | Use Case |
|---|---|---|
| IAM Policies | Attached to users, roles, groups | Control which IAM principals can call which S3 APIs |
| Bucket Policies (resource-based) | Bucket level | Public access, cross-account access, enforce encryption |
| Object ACLs | Individual object | Fine-grained per-object control (can be disabled at bucket level) |
| Block Public Access | Bucket or account level | Override setting — blocks all public access regardless of bucket policy |
Critical: Block Public Access settings override bucket policies. If Block Public Access is enabled on a bucket, a bucket policy granting public access has no effect.
Common Bucket Policy Patterns:
// Force HTTPS — deny all non-secure transport
{
"Effect": "Deny",
"Principal": "*",
"Action": "s3:*",
"Resource": ["arn:aws:s3:::my-bucket", "arn:aws:s3:::my-bucket/*"],
"Condition": { "Bool": { "aws:SecureTransport": "false" } }
}
// Force SSE-KMS encryption on all uploads
{
"Effect": "Deny",
"Principal": "*",
"Action": "s3:PutObject",
"Resource": "arn:aws:s3:::my-bucket/*",
"Condition": {
"StringNotEquals": { "s3:x-amz-server-side-encryption": "aws:kms" }
}
}
// Grant cross-account read access
{
"Effect": "Allow",
"Principal": { "AWS": "arn:aws:iam::ACCOUNT-B:root" },
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::my-bucket/*"
}
4.2 S3 Encryption Methods
| Method | Key Managed By | Header | Key Requirement |
|---|---|---|---|
| SSE-S3 | AWS (fully internal) | x-amz-server-side-encryption: AES256 |
No audit trail of key usage |
| SSE-KMS | AWS KMS (customer controls policy) | x-amz-server-side-encryption: aws:kms |
CloudTrail logs every key use |
| SSE-C | Customer (provided on every request) | Key in request header | HTTPS is mandatory |
| Client-side | Customer (before data leaves app) | None — data arrives already encrypted | AWS never sees plaintext |
Exam Rule: If the question requires an audit trail of who used which key and when → SSE-KMS.
If the question says the customer must own and manage the key entirely (not in KMS) → SSE-C (HTTPS mandatory) or Client-side encryption.
SSE-KMS at high volume:
Every SSE-KMS upload calls GenerateDataKey. Every download calls Decrypt. At tens of thousands of requests per second, this hits KMS rate limits. Solution: enable S3 Bucket Keys to generate a bucket-level data key that encrypts individual object keys locally — reduces KMS API calls by ~99%.
Critical: An S3
PutObjectwith SSE-KMS returns Access Denied when the uploading user or role lackskms:GenerateDataKeypermission on the CMK. The S3 permission alone is insufficient.
4.3 MFA Delete, Pre-signed URLs & CORS
MFA Delete:
- Requires an MFA code to permanently delete an object version or suspend versioning.
- Can only be enabled or disabled by the bucket owner using the root account credentials.
- Versioning must be enabled before MFA Delete can be configured.
Pre-signed URLs:
| Attribute | Value |
|---|---|
| Console expiry | Maximum 720 minutes (12 hours) |
| CLI expiry | Maximum 604,800 seconds (7 days) |
| IAM role-signed | Expires when the underlying STS token expires (may be less than 7 days) |
| Permissions | Inherits the permissions of the signing IAM entity — not the bucket policy |
CORS (Cross-Origin Resource Sharing):
Configured on the target bucket — the bucket that holds the assets being loaded by the browser from a different origin.
[{
"AllowedOrigins": ["https://www.example.com"],
"AllowedMethods": ["GET", "PUT"],
"AllowedHeaders": ["*"],
"MaxAgeSeconds": 3000
}]
Exam Trap: A 403 error on a cross-origin S3 request almost always means the CORS configuration is missing or incorrect on the target bucket — not an IAM permission issue.
5. Amazon Cognito Security
Cognito provides authentication and authorization for web and mobile applications. Its two components serve completely different purposes and are commonly confused on the exam.
5.1 User Pools vs Identity Pools
┌─────────────────────────────────────────────────────────────────────┐
│ Cognito — Two Services │
│ │
│ COGNITO USER POOL (CUP) COGNITO IDENTITY POOL (CIP) │
│ ───────────────────────── ────────────────────────────── │
│ AuthN — Who are you? AuthZ — What can you access? │
│ │
│ Input: Username + Password Input: A valid identity token │
│ (or social/SAML token) (from CUP, Google, SAML) │
│ │
│ Output: JWT Tokens Output: Temporary AWS credentials │
│ • ID Token via STS AssumeRole │
│ • Access Token (AccessKey + SecretKey │
│ • Refresh Token + SessionToken) │
│ │
│ Use for: Your APIs, ALB Use for: S3, DynamoDB, Kinesis │
│ API Gateway direct SDK calls │
│ │
│ Guest access: No Guest access: Yes (unauthenticated)│
└─────────────────────────────────────────────────────────────────────┘
Critical Exam Rule: A JWT token from a User Pool grants access to your API (via API Gateway Cognito Authorizer). It does NOT grant access to AWS services like S3 or DynamoDB directly. To call AWS services directly from a mobile app, you need Identity Pools to exchange the JWT for temporary AWS credentials.
Cognito User Pools — Key Features:
- Handles sign-up, sign-in, MFA, email/phone verification, and password reset.
- Federates with: Facebook, Google, Amazon, Apple, SAML 2.0 IdPs, and OIDC providers.
- MFA options: TOTP (authenticator apps) or SMS OTP.
- Adaptive authentication: assigns a risk score to each sign-in attempt and can require MFA when risk is high.
Cognito Identity Pools:
- Assigns each user a unique Identity ID regardless of which identity provider they used.
- Two IAM roles: one for authenticated users, one for unauthenticated (guest) users.
- Guest access: provide limited AWS access without any login required.
5.2 Fine-Grained Access & Lambda Triggers
Per-user resource isolation using IAM policy variables:
// Restrict each Cognito user to their own S3 folder
{
"Effect": "Allow",
"Action": ["s3:GetObject", "s3:PutObject"],
"Resource": "arn:aws:s3:::my-bucket/${cognito-identity.amazonaws.com:sub}/*"
}
// Restrict each Cognito user to their own DynamoDB rows (LeadingKeys)
{
"Effect": "Allow",
"Action": ["dynamodb:GetItem", "dynamodb:PutItem"],
"Resource": "arn:aws:dynamodb:us-east-1:*:table/UserData",
"Condition": {
"ForAllValues:StringEquals": {
"dynamodb:LeadingKeys": ["${cognito-identity.amazonaws.com:sub}"]
}
}
}
Lambda Triggers (User Pools):
| Trigger | When It Fires | Common Use Case |
|---|---|---|
| Pre Sign-Up | Before registration is confirmed | Block unwanted email domains, auto-confirm certain users |
| Pre Authentication | Before login is validated | Add custom validation logic |
| Post Confirmation | After email or phone is verified | Send a welcome email, populate a user record |
| Pre Token Generation | Before the JWT token is issued | Add, modify, or suppress claims in the token |
| Migrate User | On first login for a user not found in the pool | Silently migrate users from a legacy authentication system |
Critical: The Cognito Hosted UI custom domain requires an ACM certificate in us-east-1, regardless of the User Pool's region. This is a commonly tested trap.
6. API Gateway Security
6.1 Authorizer Types
API Gateway supports three authorizer mechanisms. Choosing the right one is a frequent exam scenario.
┌─────────────────────────────────────────────────────────────────────┐
│ API Gateway Authorizer Decision Tree │
│ │
│ Caller is an AWS service/EC2/Lambda using AWS credentials? │
│ └──► YES: IAM Authorization + SigV4 signing │
│ │
│ Caller uses a 3rd-party JWT, OAuth, or custom token? │
│ └──► YES: Lambda Authorizer (custom logic in your Lambda function) │
│ │
│ Caller is a mobile/web app user authenticated via Cognito? │
│ └──► YES: Cognito User Pool Authorizer (API GW validates JWT) │
└─────────────────────────────────────────────────────────────────────┘
| Authorizer | How It Works | Caching | Cross-Account |
|---|---|---|---|
| IAM | Caller signs request with SigV4. API GW verifies against IAM. | N/A | Yes — combine with resource policy |
| Lambda (Custom) | Lambda receives token/headers, returns an IAM policy (Allow or Deny). Policy is cached by API GW. | Yes — configurable TTL | Yes |
| Cognito | API GW validates Cognito JWT automatically. No Lambda needed. | Yes — token validation cached | No |
Exam Trap: The Cognito Authorizer handles authentication only — confirming the user is who they claim to be. Authorization (what the user can do) must be enforced in your backend application code. API Gateway does not enforce method-level permissions based on Cognito user attributes.
6.2 Resource Policies & Usage Plans
Resource Policies:
JSON policies attached directly to an API Gateway REST API. Used to:
- Restrict API access to specific AWS accounts.
- Restrict access to specific source IP ranges or CIDR blocks.
- Allow access only from specific VPCs or VPC Endpoints (for private APIs).
API Keys and Usage Plans:
Step-by-step setup (exam tests this order):
1. Create the API → configure methods to require an API key → deploy to a stage
2. Generate or import API keys
3. Create a Usage Plan (define throttle rate and monthly request quota)
4. Associate the API stage with the Usage Plan
5. Associate API Keys with the Usage Plan (via CreateUsagePlanKey API)
If step 5 is skipped → API key returns 403 Forbidden
Exam Trap: Creating an API key alone does nothing. The key must be associated with a Usage Plan via
CreateUsagePlanKey. A newly created key returns 403 until this association is made.
7. Secrets Manager & SSM Parameter Store
Two services for storing secrets and configuration. Choosing between them is a common exam scenario.
| Feature | AWS Secrets Manager | AWS SSM Parameter Store |
|---|---|---|
| Automatic rotation | Yes — built-in for RDS, Aurora, Redshift, DocumentDB | No — must build with Lambda + EventBridge |
| KMS encryption | Mandatory — always encrypted | Optional — use SecureString type for encrypted values |
| Cost | $0.40/secret/month + $0.05 per 10K API calls | Free tier available; Standard parameters are free |
| RDS native integration | Yes — rotates credentials and updates the connection | Manual |
| Cross-account access | Yes — via resource-based policy | Limited |
| Use when | Database credentials, API keys needing automatic rotation | Application config, non-rotating secrets, feature flags |
SSM Parameter Store types:
# Store a plaintext value
aws ssm put-parameter --name "/myapp/config/db-host" --value "db.example.com" --type String
# Store an encrypted secret (KMS-encrypted)
aws ssm put-parameter --name "/myapp/secrets/db-password" --value "MySecureP@ss" --type SecureString
# Retrieve and decrypt a SecureString
aws ssm get-parameter --name "/myapp/secrets/db-password" --with-decryption
Reference in buildspec.yml (CodeBuild):
env:
parameter-store:
DB_PASSWORD: /myapp/secrets/db-password
secrets-manager:
API_KEY: arn:aws:secretsmanager:us-east-1:123:secret:my-api-key
Best Practice: Never store secrets as plaintext in
env.variablesin buildspec.yml. Always useparameter-storeorsecrets-managersections.
8. Encryption at Rest & In Transit
Encryption at Rest — Service Summary
| Service | Default Encryption | Customer CMK Support |
|---|---|---|
| S3 | SSE-S3 (AES-256) | Yes — SSE-KMS |
| EBS | Not enabled by default | Yes — set at volume creation |
| RDS | Not enabled by default | Yes — set at DB instance creation |
| DynamoDB | AWS Owned keys | Yes — Customer Managed CMK |
| Lambda env vars | Lambda service key | Yes — via KMSKeyArn in function config |
| Secrets Manager | AWS Managed KMS key | Yes — specify CMK at secret creation |
| CloudWatch Logs | Not encrypted by default | Yes — CLI only (associate-kms-key) |
| SQS | Not encrypted by default | Yes — SSE via CMK |
| Kinesis | Not encrypted by default | Yes — SSE via CMK |
Critical: CloudWatch Logs KMS encryption cannot be configured from the AWS Management Console. You must use the AWS CLI command
aws logs associate-kms-keyfor existing log groups, or specify the KMS key at creation time.
# Encrypt an existing CloudWatch Logs log group (CLI only)
aws logs associate-kms-key \
--log-group-name /aws/lambda/my-function \
--kms-key-id arn:aws:kms:us-east-1:123456789012:key/abc-123
Encryption in Transit
- Force HTTPS on S3: Use a bucket policy that denies requests where
aws:SecureTransportisfalse. - SSE-C requires HTTPS: The encryption key is included in the HTTP request header — using plain HTTP would expose the key in transit.
- API Gateway: Always serves HTTPS. HTTP is not available.
- CloudFront — Viewer Protocol Policy: Set to
HTTPS OnlyorRedirect HTTP to HTTPSto enforce HTTPS between clients and CloudFront. - CloudFront — Origin Protocol Policy: Set to
HTTPS Onlyto enforce HTTPS between CloudFront and the S3 or ALB origin.
9. Network Security
9.1 Security Groups vs NACLs
| Feature | Security Groups | Network ACLs |
|---|---|---|
| State | Stateful — return traffic is automatically allowed | Stateless — return traffic must be explicitly allowed |
| Rules | Allow rules only — cannot explicitly deny | Allow and Deny rules |
| Scope | EC2 instance / ENI level | Subnet level — applies to all resources in the subnet |
| Rule evaluation | All rules evaluated together | Rules evaluated in order, lowest number first |
| Default behavior | All inbound blocked; all outbound allowed | All inbound and outbound allowed (default VPC NACL) |
| Changes take effect | Immediately | Immediately |
Exam Rule: Connection Timeout when connecting to EC2 → Security Group is blocking the traffic. Connection Refused → traffic reached EC2 but the application is not running or listening on the expected port.
9.2 VPC Endpoints & AWS WAF
VPC Endpoints — two types:
| Type | Works With | Cost | Configuration |
|---|---|---|---|
| Gateway Endpoint | S3 and DynamoDB only | Free | Added as a route table entry |
| Interface Endpoint | Most other AWS services | Hourly + data charge | Creates an ENI in your subnet with a private IP |
Use case: Lambda or ECS in a VPC needs to access S3 or DynamoDB without traversing the internet. Add a Gateway Endpoint — no NAT Gateway required. This is cheaper and more secure than routing through a NAT Gateway.
AWS WAF (Web Application Firewall):
- Attach to: ALB, API Gateway REST API, CloudFront.
- Protects against: SQL injection, XSS, HTTP floods, bad bots, geographic restrictions.
- Rules match on: IP addresses, HTTP headers, URI strings, request body content.
- Uses Web ACLs containing rules and rule groups.
VPC Flow Logs:
- Capture metadata about network traffic flowing through ENIs, subnets, or VPCs.
- Records include: source IP, destination IP, port, protocol, bytes transferred, accept/reject status.
- Does NOT capture application-layer content — it is network metadata only.
- Destination: CloudWatch Logs or S3.
10. AWS CloudTrail & Audit
10.1 Event Types & Retention
CloudTrail records all API calls made to your AWS account, regardless of whether the call came from the Console, CLI, SDK, or another AWS service.
| Event Type | Logged by Default | Description |
|---|---|---|
| Management Events | Yes | Control plane operations: creating resources, attaching policies, modifying configurations |
| Data Events | No (extra cost) | Data plane operations: S3 GetObject/PutObject, Lambda Invoke, DynamoDB GetItem |
| Insights Events | No (extra cost) | Detects unusual patterns in management event activity |
Retention:
- CloudTrail stores events for 90 days in the CloudTrail console.
- For longer retention: configure a Trail to deliver events to an S3 bucket, then analyze with Amazon Athena.
CloudTrail + EventBridge — Automated Response Pattern:
IAM user deletes a production DynamoDB table
──► CloudTrail records the DeleteTable API call
──► EventBridge rule matches on source: "aws.dynamodb" + action: "DeleteTable"
──► SNS topic sends alert to security team
──► Lambda function attempts automated remediation
10.2 CloudTrail vs CloudWatch vs X-Ray
| Dimension | CloudTrail | CloudWatch | AWS X-Ray |
|---|---|---|---|
| What it monitors | AWS API calls and account activity | Metrics, logs, alarms | Distributed request traces |
| Primary question it answers | Who did what to which resource and when? | Is my application performing within thresholds? | Where is the latency in my distributed system? |
| Used by | Security and compliance teams | Operations and development teams | Developers debugging microservices |
| Example scenario | "Who deleted the S3 bucket at 3 AM?" | "CPU utilization is above 80% — trigger an alarm" | "Why is the API response time 4 seconds?" |
| Retention | 90 days (longer via S3 Trail) | Configurable (depends on metric resolution) | 30 days |
11. Exam Tips & Quick Reference
Scenario-to-Answer Mapping
| Scenario Keyword / Requirement | Correct Answer |
|---|---|
| "Encrypt data larger than 4 KB using KMS" | GenerateDataKey — envelope encryption |
| "S3 + SSE-KMS at high throughput hitting KMS limits" | Enable S3 Bucket Keys (~99% fewer KMS calls) |
| "S3 PutObject Access Denied with SSE-KMS" | Uploading principal needs kms:GenerateDataKey on the CMK |
| "KMS key needs annual rotation" | Customer Managed CMK with automatic rotation enabled |
| "Imported KMS key rotation" | Manual only — create new key, update alias to point to it |
| "Store DB password with automatic rotation" | AWS Secrets Manager |
| "Store app config, no rotation needed" | SSM Parameter Store |
| "Developer can't launch EC2 with an IAM role" | Missing iam:PassRole permission |
| "Explicit Deny vs Allow in IAM" | Explicit Deny always wins — no exceptions |
| "EC2 has environment variables AND Instance Profile" | SDK uses environment variables (higher priority) — remove them |
| "Cross-account AWS resource access" | IAM Role in target account + sts:AssumeRole from source account |
| "Decode an encoded Access Denied error" | aws sts decode-authorization-message |
| "Determine current caller identity in CLI/SDK" | aws sts get-caller-identity |
| "Mobile app users, social login, need AWS resource access" | Cognito User Pools (authN) + Identity Pools (AWS credentials) |
| "Guest access to AWS resources without login" | Cognito Identity Pool with unauthenticated identity role |
| "Per-user DynamoDB row isolation" | IAM condition key: dynamodb:LeadingKeys = ${cognito-identity.amazonaws.com:sub} |
| "Restrict API to specific AWS account" | API Gateway resource policy |
| "Prevent privilege escalation when delegating IAM" | Permissions Boundary |
| "Audit all S3 object-level access" | CloudTrail Data Events (not enabled by default) |
| "CloudWatch Logs encryption via KMS" | CLI command aws logs associate-kms-key — not available in console |
| "Force HTTPS on S3 bucket" | Bucket policy: Deny where aws:SecureTransport is false |
| "Find users with unused permissions" | IAM Access Advisor |
| "Find users without MFA enabled" | IAM Credentials Report |
| "Who deleted the resource?" | CloudTrail Management Events |
| "New API key returns 403 Forbidden" | Call CreateUsagePlanKey to associate the key with a usage plan |
Common Traps
- iam:PassRole is separate from service permissions: Having full EC2 or Lambda permissions does not include the ability to assign an IAM role to those services.
iam:PassRoleis a distinct permission that must be explicitly granted. - Cognito User Pools produce JWTs, not AWS credentials: A JWT from a User Pool lets you call your own API. To call AWS services directly (S3, DynamoDB), exchange the JWT for temporary credentials via a Cognito Identity Pool.
- CloudWatch Logs KMS via CLI only: The AWS Management Console does not support associating a KMS key with a CloudWatch Logs log group. This must be done via the
associate-kms-keyCLI command. - SSE-C requires HTTPS: When using server-side encryption with a customer-provided key, the key is included in the request header. Using plain HTTP would expose the key in transit. HTTPS is mandatory.
- S3 Bucket Key reduces CloudTrail events too: When S3 Bucket Keys are enabled, KMS generates one data key per bucket per time period instead of one per object. This reduces both KMS API calls and the number of CloudTrail log entries for KMS key usage.
- Block Public Access overrides bucket policies: A bucket policy granting public access is irrelevant if Block Public Access is enabled. The Block Public Access setting wins.
- Permissions Boundary does not grant permissions: A boundary defines the maximum possible permissions. An identity still needs an explicit Allow in their identity policy. Boundary alone = no access.
Key Terms — Domain 2
| Term | One-Line Definition |
|---|---|
| Explicit Deny | An IAM statement with "Effect": "Deny" — always overrides any Allow |
| iam:PassRole | Permission required to assign an IAM role to an AWS service |
| Permissions Boundary | Managed policy that sets the maximum permissions an identity can have |
| Envelope Encryption | Pattern where a data key (DEK) encrypts data and a master key (CMK) encrypts the DEK |
| SSE-KMS | S3 server-side encryption using a KMS-managed key — provides CloudTrail audit trail |
| SSE-C | S3 server-side encryption with a customer-provided key — HTTPS mandatory |
| Customer Managed CMK | KMS key you create and control — supports key policies, rotation, and deletion |
| Credential Chain | The ordered list of locations the AWS CLI and SDK check for credentials |
| Resource-based Policy | Policy attached to an AWS resource (S3, SQS, Lambda) that includes a Principal |
| Cognito User Pool | User directory that handles sign-up/sign-in and issues JWT tokens |
| Cognito Identity Pool | Federates identities and issues temporary AWS credentials via STS |
| STS AssumeRole | API call that exchanges identity for temporary credentials scoped to an IAM role |
| CloudTrail Data Events | High-volume events (S3 object access, Lambda invocations) — not logged by default |
| VPC Gateway Endpoint | Free VPC endpoint for S3 and DynamoDB — no NAT Gateway required |
| MFA Delete | S3 versioning protection — requires root account MFA to permanently delete versions |
End of Domain 2: Security. Continue to Domain 3: Deployment →
Ready to test yourself?
Practice questions for this topic