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 — Deep Dive
- AWS STS — Temporary Credentials
- Amazon Cognito
- AWS KMS — Key Management Service
- AWS Secrets Manager
- AWS Systems Manager — Parameter Store
- S3 Encryption — All Options
- API Gateway Security
- Lambda Security
- AWS WAF — Web Application Firewall
- AWS ACM — Certificate Manager
- SigV4 — Request Signing
- Exam Tips & Quick Reference
1. AWS IAM — Deep Dive
IAM controls WHO (authentication) can do WHAT (authorization) on which AWS resources. Understanding IAM policy evaluation is the single most tested security concept across all AWS certification exams.
1.1 Principals, Identities & Policies
Principals — entities that can make requests to AWS:
| Principal | Description |
|---|---|
| IAM User | Long-term credentials (access key + secret). One per human or service account |
| IAM Group | Collection of users; policies attached to group apply to all members |
| IAM Role | Temporary credentials; assumed by users, services, or applications |
| AWS Service | EC2, Lambda, ECS — assume roles to call AWS APIs |
| Federated Identity | External users authenticated via SAML 2.0, OIDC, or Web Identity |
IAM Policy Types:
| Policy Type | Attached To | Purpose |
|---|---|---|
| Identity-Based | User, Group, Role | Grants permissions to the principal |
| Resource-Based | S3, SQS, KMS, Lambda, etc. | Grants access FROM specific principals TO the resource |
| Permission Boundary | User or Role | Sets the maximum permissions an identity can have |
| SCP (Service Control Policy) | AWS Organizations OU/Account | Sets the maximum for the entire account |
| Session Policy | STS AssumeRole call | Restricts permissions within a temporary session |
| ACL (Access Control List) | S3, VPC | Legacy; controls cross-account access at the bucket/object level |
1.2 IAM Policy Evaluation Logic
This is the most commonly tested concept in security questions. Understanding the order of evaluation is critical.
┌─────────────────────────────────────────────────────────────────────────────┐
│ IAM Policy Evaluation Logic │
│ │
│ Request Comes In │
│ │ │
│ ▼ │
│ ┌──────────────────────┐ │
│ │ 1. Explicit DENY? │── YES ──► DENY (Explicit Deny always wins) │
│ │ (any policy) │ │
│ └──────────┬───────────┘ │
│ │ NO │
│ ▼ │
│ ┌──────────────────────┐ │
│ │ 2. SCP in Org? │── Doesn't allow ──► DENY │
│ └──────────┬───────────┘ │
│ │ Allows │
│ ▼ │
│ ┌──────────────────────┐ │
│ │ 3. Resource-Based │── Allows (same account) ──► ALLOW │
│ │ Policy? │── Allows (cross-account) ──► Check identity policy│
│ └──────────┬───────────┘ │
│ │ No resource-based policy │
│ ▼ │
│ ┌──────────────────────┐ │
│ │ 4. Permission │── Does NOT allow ──► DENY │
│ │ Boundary? │ │
│ └──────────┬───────────┘ │
│ │ Allows │
│ ▼ │
│ ┌──────────────────────┐ │
│ │ 5. Session Policy? │── Does NOT allow ──► DENY │
│ └──────────┬───────────┘ │
│ │ Allows │
│ ▼ │
│ ┌──────────────────────┐ │
│ │ 6. Identity-Based │── Yes ──► ALLOW │
│ │ Policy Allows? │── No ──► DENY (implicit deny) │
│ └──────────────────────┘ │
└─────────────────────────────────────────────────────────────────────────────┘
The Golden Rules:
- Explicit DENY always overrides any ALLOW — in any policy.
- Default is DENY — without an explicit allow, access is denied.
- SCPs set the ceiling — an SCP cannot grant permissions, it can only restrict.
- Resource-Based Policy allows same-account access without needing identity-based policy.
- Cross-account access requires BOTH the resource-based policy (or role trust policy) AND the identity-based policy on the requesting account.
1.3 IAM Roles & Trust Policies
A role has two policies:
- Trust Policy — defines WHO can assume this role (the principal).
- Permissions Policy — defines WHAT the role can do (the actions/resources).
// Trust Policy — allows EC2 service to assume this role
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": "ec2.amazonaws.com"
},
"Action": "sts:AssumeRole"
}
]
}
// Trust Policy — allows cross-account role assumption
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::ACCOUNT-B-ID:root"
},
"Action": "sts:AssumeRole",
"Condition": {
"StringEquals": {
"sts:ExternalId": "UniqueExternalId123"
}
}
}
]
}
ExternalId is used to prevent the Confused Deputy Problem — a scenario where an attacker tricks your trusted third-party service into accessing your resources using the third party's permissions.
1.4 Resource-Based Policies vs Identity-Based Policies
| Dimension | Identity-Based Policy | Resource-Based Policy |
|---|---|---|
| Attached to | IAM User, Group, Role | Resource (S3, SQS, KMS, Lambda, etc.) |
| Specifies Principal | No (the principal is implied by attachment) | Yes (explicitly names WHO can access) |
| Cross-account | Must be combined with resource-based policy | Can grant access on its own (same account or cross-account) |
| Services that support it | All | S3, SQS, SNS, KMS, Lambda, API Gateway, Secrets Manager, ECR, and others |
Critical Cross-Account Rule: To allow Account A to access an S3 bucket in Account B:
- Account B bucket policy must allow Account A's principal.
- Account A identity-based policy must allow the S3 action.
Both must allow. One allow is not enough for cross-account.
1.5 IAM Policy Conditions & Permission Boundaries
Policy Conditions add fine-grained control:
| Condition Key | Example Use |
|---|---|
aws:RequestedRegion |
Restrict actions to specific regions |
aws:PrincipalTag |
Control based on IAM tags on the principal |
aws:ResourceTag |
Control based on tags on the resource |
aws:SecureTransport |
Require HTTPS (TLS) for S3 requests |
aws:MultiFactorAuthPresent |
Require MFA for sensitive operations |
s3:prefix |
Restrict S3 access to a specific key prefix |
iam:PassedToService |
Control which services a role can be passed to |
// Deny all S3 actions unless request uses TLS
{
"Effect": "Deny",
"Action": "s3:*",
"Resource": "*",
"Condition": {
"Bool": {
"aws:SecureTransport": "false"
}
}
}
Permission Boundaries:
A permission boundary is an identity-based policy that sets the maximum permissions a user or role can have. It does NOT grant permissions on its own — it only restricts the effective permissions.
Effective Permissions = Identity Policy ∩ Permission Boundary
Example:
Identity Policy: Allow S3:*, EC2:*, DynamoDB:*
Permission Boundary: Allow S3:*, EC2:*
Effective: Allow S3:*, EC2:* (DynamoDB removed by boundary)
Exam Tip: Permission boundaries are commonly used to allow developers to create IAM roles for Lambda functions without being able to grant themselves permissions beyond what they have. A developer can only grant a role permissions that fall within their own permission boundary.
2. AWS STS — Temporary Credentials
AWS Security Token Service (STS) issues temporary, limited-privilege credentials. These consist of: Access Key ID, Secret Access Key, and a Session Token. Temporary credentials expire automatically.
2.1 AssumeRole, AssumeRoleWithWebIdentity & AssumeRoleWithSAML
| STS Action | Use Case | Who Calls It |
|---|---|---|
AssumeRole |
Cross-account access; EC2/Lambda service roles | IAM users, roles, AWS services |
AssumeRoleWithWebIdentity |
Mobile/web app users authenticated by OIDC providers (Google, Facebook, Apple, Cognito) | Application using ID token |
AssumeRoleWithSAML |
Corporate users authenticated by SAML 2.0 IdP (Okta, ADFS) | SAML assertion from IdP |
GetSessionToken |
MFA-protected API calls; already-authenticated IAM users | IAM users needing MFA temp creds |
GetFederationToken |
Broker-based federation (older pattern) | IAM users creating federation tokens |
AssumeRoleWithWebIdentity Flow (Modern Apps):
1. User authenticates with Google/Facebook → receives ID Token (JWT)
2. App calls STS:AssumeRoleWithWebIdentity(roleArn, webIdentityToken)
3. STS validates token with the OIDC provider
4. STS returns temporary credentials (AccessKey, SecretKey, SessionToken)
5. App uses temporary credentials to call AWS services directly
Note: AWS now recommends using Cognito Identity Pools instead of calling
AssumeRoleWithWebIdentity directly in mobile/web apps.
2.2 Session Policies & Credential Duration
Session Policies are passed inline during AssumeRole to further restrict permissions within the session. They cannot expand permissions beyond the role's permissions policy.
# AssumeRole with an inline session policy (further restrict access)
response = sts_client.assume_role(
RoleArn='arn:aws:iam::123456789:role/MyRole',
RoleSessionName='my-session',
DurationSeconds=3600,
Policy=json.dumps({
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Action": ["s3:GetObject"],
"Resource": "arn:aws:s3:::my-bucket/*"
}]
})
)
credentials = response['Credentials']
| Duration Setting | Range | Default |
|---|---|---|
| AssumeRole | 900s – 43200s (1 hour – 12 hours) | 1 hour |
| AssumeRoleWithWebIdentity | 900s – 43200s | 1 hour |
| GetSessionToken | 900s – 129600s (up to 36 hours) | 12 hours |
3. Amazon Cognito
Cognito is AWS's fully managed authentication, authorization, and user management service for web and mobile applications.
3.1 User Pools
A User Pool is a user directory that provides sign-up, sign-in, and profile management. It issues JWT tokens (ID token, access token, refresh token).
┌────────────────────────────────────────────────────────────────────┐
│ Cognito User Pool │
│ │
│ ┌───────────┐ Sign-up/Sign-in ┌───────────────────────────┐ │
│ │ User │─────────────────────►│ User Pool │ │
│ │ (Browser/│ │ │ │
│ │ Mobile) │◄─────────────────────│ Issues JWT Tokens: │ │
│ └───────────┘ Returns tokens │ • ID Token (user info) │ │
│ │ • Access Token (scopes) │ │
│ Federated IdPs: │ • Refresh Token │ │
│ • Google, Facebook, Apple └───────────────────────────┘ │
│ • SAML 2.0 (Okta, ADFS) │
│ • OIDC providers │
└────────────────────────────────────────────────────────────────────┘
Key User Pool Features:
| Feature | Description |
|---|---|
| MFA | SMS-based or TOTP (time-based one-time password) |
| Lambda Triggers | Pre/Post authentication, pre/post sign-up, token generation, custom messages |
| Hosted UI | Pre-built, customizable sign-in page |
| App Clients | Each app that accesses the user pool needs an app client |
| User Groups | Assign users to groups; groups can map to IAM roles |
| Password Policy | Configure minimum length, complexity, expiry |
Cognito Lambda Triggers (commonly tested):
| Trigger | When It Fires | Use Case |
|---|---|---|
| Pre Sign-up | Before a new user is saved | Validate email domain, auto-confirm users |
| Post Confirmation | After user confirms | Add user to database |
| Pre Authentication | Before every sign-in | Block sign-in based on custom logic |
| Post Authentication | After successful sign-in | Log sign-in events |
| Pre Token Generation | Before JWT issued | Add/modify/suppress claims in the token |
| Custom Message | Before sending SMS/email | Customize verification messages |
| Migrate User | If user not found in pool | Migrate users from legacy system on first login |
3.2 Identity Pools (Federated Identities)
An Identity Pool provides temporary AWS credentials to authenticated or unauthenticated (guest) users, enabling direct access to AWS services.
┌────────────────────────────────────────────────────────────────────────┐
│ Cognito Identity Pool Flow │
│ │
│ 1. User authenticates with: │
│ • Cognito User Pool │
│ • Google / Facebook / Apple (OIDC) │
│ • SAML IdP │
│ • Custom Developer Authenticated Identity │
│ │
│ 2. App sends token to Cognito Identity Pool │
│ │
│ 3. Identity Pool calls STS:AssumeRoleWithWebIdentity │
│ │
│ 4. STS returns temporary AWS credentials │
│ (AccessKey + SecretKey + SessionToken) │
│ │
│ 5. App uses credentials to access S3, DynamoDB, API Gateway, etc. │
└────────────────────────────────────────────────────────────────────────┘
Authenticated vs Unauthenticated (Guest) Roles:
- Authenticated role: Assigned to verified users. Broader permissions.
- Unauthenticated role: Assigned to anonymous (guest) users. Minimal permissions. Can be disabled.
Key Concept: Cognito User Pools = Authentication (who you are). Identity Pools = Authorization (what AWS resources you can access). They are separate services that work together.
3.3 User Pool + Identity Pool Combined Flow
This is the most common real-world pattern and the most frequently tested combined flow:
Mobile App
│
│ 1. Sign in
▼
Cognito User Pool ──► Returns ID Token (JWT)
│
│ 2. Exchange ID Token
▼
Cognito Identity Pool ──► Calls STS ──► Returns AWS Temp Credentials
│
│ 3. Use credentials
▼
AWS Services (S3, DynamoDB, API Gateway)
User Pool Groups → Identity Pool IAM Role Mapping:
Assign users to User Pool Groups. Map each group to a specific IAM Role in the Identity Pool. Users in the "Admin" group get the AdminRole; users in the "Users" group get the UserRole.
3.4 Cognito Sync & AppSync
| Service | Purpose |
|---|---|
| Cognito Sync | Deprecated. Synchronize user profile data across devices. |
| AWS AppSync | Recommended replacement. Managed GraphQL API with real-time subscriptions and Cognito integration. |
4. AWS KMS — Key Management Service
KMS is a managed service for creating and controlling cryptographic keys. All KMS operations are logged in CloudTrail — every Encrypt, Decrypt, GenerateDataKey call is auditable.
4.1 Key Types
┌─────────────────────────────────────────────────────────────────────┐
│ KMS Key Types │
│ │
│ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────────┐ │
│ │ AWS Managed Key │ │ Customer Managed│ │ AWS Owned Key │ │
│ │ (aws/s3, aws/rds)│ │ Key (CMK) │ │ (SSE-S3 default) │ │
│ ├─────────────────┤ ├─────────────────┤ ├─────────────────────┤ │
│ │ Free │ │ $1/month │ │ Free, no visibility │ │
│ │ No key policy │ │ Full key policy │ │ Fully managed by AWS│ │
│ │ control │ │ control │ │ │ │
│ │ Rotated: yearly │ │ Rotated: yearly │ │ Rotated by AWS │ │
│ │ (auto, mandatory)│ │ (opt-in auto or │ │ │ │
│ │ │ │ manual) │ │ │ │
│ └─────────────────┘ └─────────────────┘ └─────────────────────┘ │
└─────────────────────────────────────────────────────────────────────┘
Key Material Origin:
| Origin | Description |
|---|---|
| KMS (default) | AWS generates and manages key material in HSMs |
| External (BYOK) | You import your own key material. You manage rotation manually |
| Custom Key Store (CloudHSM) | Key material stored in your dedicated HSM cluster |
Key Policies: Every KMS key has exactly one key policy. Without a key policy statement explicitly allowing the AWS account, no one can use the key — even the account root user. This is unlike IAM policies.
Critical: The default KMS key policy allows the AWS account root to administer the key, which then allows IAM policies to control KMS access. If you create a CMK and DON'T add
"Principal": {"AWS": "arn:aws:iam::ACCOUNT_ID:root"}to the key policy, even the root user cannot use the key.
4.2 Envelope Encryption
KMS has a 4 KB limit per encrypt/decrypt call. For encrypting large data, AWS uses Envelope Encryption:
┌─────────────────────────────────────────────────────────────────────────┐
│ Envelope Encryption Flow │
│ │
│ ENCRYPT: │
│ 1. Call KMS GenerateDataKey → returns plaintext DEK + encrypted DEK │
│ 2. Use PLAINTEXT DEK to encrypt your data locally (AES-256-GCM) │
│ 3. Store ENCRYPTED DEK alongside the encrypted data │
│ 4. Immediately discard the plaintext DEK from memory │
│ │
│ DECRYPT: │
│ 1. Read encrypted DEK from storage │
│ 2. Call KMS Decrypt(encryptedDEK) → returns plaintext DEK │
│ 3. Use plaintext DEK to decrypt your data locally │
│ 4. Discard plaintext DEK │
│ │
│ Result: KMS never sees your data. Only the DEK is managed by KMS. │
│ KMS CMK ──encrypts──► DEK ──encrypts──► Your Data │
└─────────────────────────────────────────────────────────────────────────┘
KMS API Operations:
| API | Description | Key Distinction |
|---|---|---|
Encrypt |
Encrypt up to 4 KB of plaintext using CMK | Direct encryption, no DEK |
Decrypt |
Decrypt ciphertext using CMK | KMS determines the key from ciphertext |
GenerateDataKey |
Returns both plaintext and encrypted DEK | Envelope encryption start |
GenerateDataKeyWithoutPlaintext |
Returns only encrypted DEK | Defer decryption to later |
ReEncrypt |
Decrypt then re-encrypt with a different key without exposing plaintext | Key rotation |
DescribeKey |
Get key metadata | |
CreateKey |
Create a new CMK |
Exam Tip:
GenerateDataKeyWithoutPlaintextis used when you want to generate and store the encrypted DEK now but do the actual encryption later (e.g., batch jobs that encrypt overnight).
4.3 KMS API & Key Policies
KMS Grants are an alternative to key policies for programmatic, temporary delegation of key use.
# Create a grant that allows a Lambda function to use a key
kms_client.create_grant(
KeyId='arn:aws:kms:...',
GranteePrincipal='arn:aws:iam::123456789:role/LambdaRole',
Operations=['Decrypt', 'GenerateDataKey']
)
KMS Key Alias: Human-readable names for keys (alias/my-app-key). Can be updated to point to a different key — allowing key rotation without changing application code.
4.4 KMS Multi-Region Keys
Multi-Region Keys allow you to encrypt data in one region and decrypt it in another without re-encrypting.
Primary Key Replica Keys
us-east-1 ──(sync key material)──► eu-west-1
──► ap-southeast-1
Same key ID prefix: mrk-...
Encrypt in us-east-1, decrypt in eu-west-1 without calling KMS cross-region
Use Case: Global DynamoDB tables, Aurora Global Databases, S3 replication — any scenario where data is replicated cross-region and must be decryptable in the replica region.
5. AWS Secrets Manager
Secrets Manager stores, rotates, manages, and retrieves database credentials, API keys, and other secrets. It integrates with Lambda for automatic rotation.
5.1 Secret Rotation
Automatic rotation uses a Lambda rotation function that AWS provides for supported databases (RDS MySQL, PostgreSQL, Oracle, SQL Server, Redshift, DocumentDB).
Rotation Flow:
1. Secrets Manager triggers rotation Lambda (on schedule or on demand)
2. Lambda creates a new password (AWSPENDING version)
3. Lambda updates the database with the new password
4. Lambda tests the new credentials
5. Lambda updates the AWSCURRENT version to point to new creds
6. AWSPREVIOUS version retained for safety
Accessing Secrets in Code:
import boto3
import json
def get_secret():
client = boto3.client('secretsmanager')
response = client.get_secret_value(SecretId='prod/myapp/dbpassword')
secret = json.loads(response['SecretString'])
return secret['password']
# Best Practice: Cache the secret in memory; re-fetch only on auth failure
# Avoid calling GetSecretValue on every request
5.2 Secrets Manager vs SSM Parameter Store
| Feature | Secrets Manager | SSM Parameter Store |
|---|---|---|
| Cost | $0.40/secret/month + $0.05/10K API calls | Free (Standard Tier); $0.05/10K API calls (Advanced) |
| Automatic Rotation | Yes — built-in with Lambda | No (requires custom Lambda) |
| Secret Versioning | Yes (AWSCURRENT, AWSPENDING, AWSPREVIOUS) | Yes |
| KMS Encryption | Always (KMS mandatory) | Optional (SecureString requires KMS) |
| Max Size | 65 KB | 4 KB (Standard), 8 KB (Advanced) |
| Cross-Account | Yes (resource policy) | No |
| Hierarchical Paths | No | Yes (/myapp/prod/dbpassword) |
| CloudFormation | dynamic reference: {{resolve:secretsmanager:...}} |
{{resolve:ssm-secure:...}} |
| Best For | Database passwords, API keys (needs rotation) | App config, feature flags, non-sensitive config |
Decision Rule: If it needs automatic rotation → Secrets Manager. If it's configuration (with or without encryption, no rotation) → SSM Parameter Store. Secrets Manager is more expensive but rotation is its key differentiator.
6. AWS Systems Manager — Parameter Store
Parameter Store provides secure, hierarchical storage for configuration data and secrets.
6.1 Parameter Tiers & Types
| Parameter Type | Encrypted | Use Case |
|---|---|---|
String |
No | Non-sensitive config (region, table names) |
StringList |
No | Comma-separated values |
SecureString |
Yes (KMS) | Passwords, API keys |
| Tier | Max Size | Cost | Policies |
|---|---|---|---|
| Standard | 4 KB | Free | No |
| Advanced | 8 KB | $0.05/10K calls | Yes (TTL, notification) |
Hierarchical Parameters:
/myapp/dev/database/host
/myapp/dev/database/password
/myapp/prod/database/host
/myapp/prod/database/password
Retrieve all parameters for an environment in one call:
response = ssm_client.get_parameters_by_path(
Path='/myapp/prod/',
Recursive=True,
WithDecryption=True # Required for SecureString
)
CloudFormation Dynamic References:
# Reference SSM Parameter in CloudFormation template
MyFunction:
Type: AWS::Serverless::Function
Properties:
Environment:
Variables:
DB_HOST: '{{resolve:ssm:/myapp/prod/database/host}}'
DB_PASS: '{{resolve:ssm-secure:/myapp/prod/database/password}}' # KMS decrypted
7. S3 Encryption — All Options
7.1 Server-Side Encryption (SSE)
Encryption is performed by S3 on the server side. Since November 2023, all new S3 objects are encrypted by default (SSE-S3).
┌─────────────────────────────────────────────────────────────────────────────┐
│ S3 Server-Side Encryption Options │
│ │
│ ┌────────────────┐ ┌────────────────┐ ┌────────────────┐ │
│ │ SSE-S3 │ │ SSE-KMS │ │ SSE-C │ │
│ │ (AES-256) │ │ (KMS CMK) │ │ (Customer Key) │ │
│ ├────────────────┤ ├────────────────┤ ├────────────────┤ │
│ │ • Default │ │ • You control │ │ • You provide │ │
│ │ since 2023 │ │ the key │ │ key per req │ │
│ │ • No extra cost │ │ • Audit in │ │ • AWS never │ │
│ │ • Header: │ │ CloudTrail │ │ stores key │ │
│ │ AES256 │ │ • Header: │ │ • HTTPS only │ │
│ │ │ │ aws:kms │ │ • Not for CLI/ │ │
│ │ │ │ • KMS limits │ │ console │ │
│ │ │ │ apply │ │ │ │
│ └────────────────┘ └────────────────┘ └────────────────┘ │
└─────────────────────────────────────────────────────────────────────────────┘
| Option | Header | Key Managed By | Key Stored By |
|---|---|---|---|
| SSE-S3 | x-amz-server-side-encryption: AES256 |
AWS | AWS |
| SSE-KMS | x-amz-server-side-encryption: aws:kms |
You | AWS (KMS) |
| SSE-C | x-amz-server-side-encryption-customer-algorithm: AES256 + key |
You | NOT stored (you send per request) |
Critical SSE-KMS Consideration: Every PUT and GET on an SSE-KMS object calls KMS. For high-throughput S3 buckets, this can exhaust KMS API limits (10,000 requests/second default). Request a KMS quota increase if needed.
SSE-KMS with DSSE-KMS (Dual-Layer): Applies two layers of SSE-KMS encryption. Used for compliance requirements (e.g., CNSSI 1253). Each object has two different data keys.
7.2 Client-Side Encryption (CSE)
Data is encrypted before being uploaded to S3. AWS never sees plaintext.
| CSE Method | Key Source | Use Case |
|---|---|---|
| CSE-KMS | KMS CMK | Use AWS SDK + KMS. AWS Encryption SDK handles envelope encryption. |
| CSE-Custom | Your own key management | Full key control; you manage everything |
7.3 Bucket Policies to Enforce Encryption
Deny unencrypted uploads:
{
"Effect": "Deny",
"Principal": "*",
"Action": "s3:PutObject",
"Resource": "arn:aws:s3:::my-bucket/*",
"Condition": {
"StringNotEquals": {
"s3:x-amz-server-side-encryption": "aws:kms"
}
}
}
Deny non-TLS requests:
{
"Effect": "Deny",
"Principal": "*",
"Action": "s3:*",
"Resource": ["arn:aws:s3:::my-bucket", "arn:aws:s3:::my-bucket/*"],
"Condition": {
"Bool": {
"aws:SecureTransport": "false"
}
}
}
8. API Gateway Security
8.1 IAM Authorization (SigV4)
Use IAM authorization when the caller is an AWS service (EC2, Lambda, another service) or a trusted internal user with AWS credentials.
- The caller signs the request with SigV4 using their IAM credentials.
- API Gateway validates the signature and checks IAM permissions.
- The IAM policy must allow
execute-api:Invokeon the API's ARN. - Resource format:
arn:aws:execute-api:REGION:ACCOUNT_ID:API_ID/STAGE/METHOD/RESOURCE
// IAM policy allowing API Gateway invocation
{
"Effect": "Allow",
"Action": "execute-api:Invoke",
"Resource": "arn:aws:execute-api:us-east-1:123456789:abc123def/prod/GET/users"
}
8.2 Lambda Authorizer (Custom Authorizer)
A Lambda function that validates any bearer token (JWT, OAuth, custom) or request parameters before API Gateway forwards the request to the backend.
Two Types:
| Type | Validates | Caching Key |
|---|---|---|
| Token-based | Bearer token in Authorization header |
Token value |
| Request-based | Headers, query params, path params, stage vars | Combination of parameters |
Client ──► API Gateway ──► Lambda Authorizer
│ │
│ (validate token)
│ │
│ Return IAM Policy
│ │
◄───────────────┘
│
If Allow → forward to backend
If Deny → return 403
Authorizer Response — IAM Policy:
{
"principalId": "user-001",
"policyDocument": {
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Action": "execute-api:Invoke",
"Resource": "arn:aws:execute-api:us-east-1:*:*/*/*/*"
}]
},
"context": {
"userId": "user-001",
"role": "admin"
}
}
Caching: Lambda Authorizer results are cached by default (TTL up to 3600 seconds). The cache key is the token value (for token-based) or the combination of parameters (for request-based). Setting TTL=0 disables caching.
8.3 Cognito User Pool Authorizer
API Gateway natively validates Cognito User Pool JWTs (access tokens or ID tokens). No Lambda function needed.
Client ──► Sign in to Cognito User Pool ──► Receives JWT
──► API Gateway (Authorization: Bearer <JWT>)
──► API Gateway validates JWT with Cognito
──► Forward to Lambda if valid
Comparison:
| Authorizer | Backend Needed | Supports | Use Case |
|---|---|---|---|
| IAM | No (native) | AWS internal callers | Service-to-service |
| Lambda | Yes (your Lambda) | Any token/auth scheme | Custom OAuth, API keys, complex logic |
| Cognito | No (native) | Cognito User Pool JWTs | Mobile/web apps using Cognito |
Exam Trap: Cognito User Pool Authorizer validates the token format and signature but does NOT check IAM permissions. Lambda Authorizer returns an IAM policy that is then evaluated. They work differently despite both being "authorizers."
9. Lambda Security
9.1 Execution Role & Resource-Based Policy
Execution Role: An IAM role that Lambda assumes when it runs. Grants Lambda permission to call other AWS services (write to DynamoDB, publish to SNS, write logs to CloudWatch). Every Lambda function must have an execution role.
// Minimum execution role trust policy (required)
{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Principal": {"Service": "lambda.amazonaws.com"},
"Action": "sts:AssumeRole"
}]
}
// Minimum permissions (allow CloudWatch Logs)
{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Action": [
"logs:CreateLogGroup",
"logs:CreateLogStream",
"logs:PutLogEvents"
],
"Resource": "arn:aws:logs:*:*:*"
}]
}
Resource-Based Policy (Lambda): Controls which principals can invoke the function. When API Gateway, S3, SNS, or EventBridge triggers Lambda, AWS automatically adds a resource-based policy statement granting that service permission to invoke the function.
// Lambda resource-based policy: allow API Gateway to invoke
{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Principal": {"Service": "apigateway.amazonaws.com"},
"Action": "lambda:InvokeFunction",
"Resource": "arn:aws:lambda:us-east-1:123456789:function:MyFunction",
"Condition": {
"ArnLike": {
"AWS:SourceArn": "arn:aws:execute-api:us-east-1:123456789:api-id/*"
}
}
}]
}
9.2 Lambda in a VPC
By default, Lambda runs in an AWS-managed VPC with internet access. When you place Lambda in your VPC, it can access private resources (RDS, ElastiCache, private ALB) but loses direct internet access.
Lambda in VPC:
✅ Can access: RDS, ElastiCache, private EC2, private ALB
❌ Cannot access: Internet, AWS public endpoints (S3, DynamoDB, STS, etc.)
Solutions to restore access:
• Internet access → NAT Gateway in a public subnet + Lambda in private subnet
• AWS services → VPC Gateway Endpoints (S3, DynamoDB) or Interface Endpoints (VPC PrivateLink)
VPC Configuration Requirements:
- Subnet(s) — must be private subnets for security.
- Security Group — controls inbound/outbound from Lambda's ENI.
- Lambda creates an Elastic Network Interface (ENI) in your subnet. ENI creation time was previously a cold start contributor, but AWS now pre-creates ENIs (Hyperplane ENIs), eliminating this overhead.
Classic Exam Scenario: "Lambda times out connecting to RDS." → Check: Lambda is in the same VPC? Lambda's security group allows outbound to RDS port? RDS security group allows inbound from Lambda's security group?
10. AWS WAF — Web Application Firewall
WAF protects web applications from common exploits (SQL injection, XSS, bot traffic). It works at Layer 7.
Can be attached to:
- CloudFront distributions
- Application Load Balancer (ALB)
- API Gateway (REST API)
- AWS AppSync
- Cognito User Pool
WAF Components:
| Component | Description |
|---|---|
| Web ACL | The container for rules. Attached to a CloudFront, ALB, or API GW. |
| Rule Group | Reusable collection of rules |
| AWS Managed Rules | Pre-built rule groups (OWASP Top 10, AWS IP Reputation List, Bot Control) |
| Rate-Based Rule | Block IPs exceeding a request threshold (minimum: 100 req/5 min) |
| IP Set | Allow or block specific IP addresses or CIDR ranges |
| Regex Pattern Set | Match request components against regex patterns |
Rule Actions:
| Action | Effect |
|---|---|
| Allow | Forward the request |
| Block | Return 403 Forbidden |
| Count | Count the request (monitoring only, no block) |
| CAPTCHA | Verify human with a challenge |
Exam Tip: WAF rate-based rules are the correct answer for "block users who are making too many requests" (DDoS/brute force scenarios at Layer 7). For Layer 3/4 DDoS protection, AWS Shield is the answer.
11. AWS ACM — Certificate Manager
ACM provisions, manages, and deploys public SSL/TLS certificates for AWS services.
| Feature | Detail |
|---|---|
| Public certificates | Free; auto-renewed by ACM |
| Private certificates | ACM Private CA (paid) |
| Integration | CloudFront, ALB, API Gateway, CloudWatch, Elastic Beanstalk |
| Import | Import third-party certificates (you manage renewal) |
| Validation | DNS validation (preferred, auto-renews) or Email validation |
Critical Regional Constraint: For CloudFront, certificates must be in us-east-1 (N. Virginia), regardless of where your CloudFront distribution is deployed. For ALB or API Gateway, the certificate must be in the same region as the load balancer/API.
12. SigV4 — Request Signing
AWS Signature Version 4 (SigV4) is the process of signing HTTP requests to AWS services using your AWS credentials to prove authenticity.
Components of a SigV4 Signature:
Authorization: AWS4-HMAC-SHA256
Credential=ACCESS_KEY_ID/DATE/REGION/SERVICE/aws4_request,
SignedHeaders=content-type;host;x-amz-date,
Signature=CALCULATED_HMAC_SIGNATURE
Signing Flow:
1. Create Canonical Request
(method + URI + query string + headers + hashed payload)
2. Create String to Sign
(algorithm + datetime + credential scope + hash of canonical request)
3. Calculate Signature
HMAC-SHA256(SigningKey, StringToSign)
where SigningKey = HMAC(HMAC(HMAC(HMAC("AWS4"+SecretKey, Date), Region), Service), "aws4_request")
4. Add to Authorization header or as query string parameters (presigned URLs)
Where SigV4 is Used:
- All AWS API calls (CLI, SDK automatically sign requests)
- API Gateway with IAM Authorization
- S3 pre-signed URLs
- CloudFront Signed URLs/Cookies use their own mechanism (different from SigV4)
Exam Tip: If a question asks "how does a service prove its identity when calling another AWS service without storing credentials," the answer involves an IAM Role (the service assumes the role) with STS temporary credentials, and requests are signed with SigV4.
13. Exam Tips & Quick Reference
Scenario-to-Answer Mapping
| Scenario Keyword / Requirement | Correct Answer |
|---|---|
| "Auto-rotate database credentials" | Secrets Manager with automatic rotation |
| "Store encrypted app config; no rotation needed" | SSM Parameter Store (SecureString) |
| "Mobile users authenticate with Google, then access S3 directly" | Cognito Identity Pool + AssumeRoleWithWebIdentity |
| "Add user directory, sign-up/sign-in, JWT tokens" | Cognito User Pool |
| "Validate custom API token before API Gateway routes request" | Lambda Authorizer |
| "Validate Cognito JWT in API Gateway natively" | Cognito User Pool Authorizer |
| "Service-to-service API Gateway calls (EC2 → API GW)" | IAM Authorization + SigV4 |
| "Encrypt S3 objects with your own KMS key; audit access" | SSE-KMS |
| "Encrypt S3 objects; AWS manages key entirely" | SSE-S3 |
| "Encrypt large data (>4 KB) with KMS" | Envelope Encryption (GenerateDataKey) |
| "Share encrypted data across regions without re-encrypting" | KMS Multi-Region Keys |
| "Block SQL injection attacks on API Gateway" | AWS WAF |
| "Lambda cannot access RDS; connection timeout" | Lambda in VPC; check SG rules |
| "Lambda needs internet AND access to private RDS" | Lambda in private subnet + NAT Gateway |
| "Prevent a developer from granting themselves more than their permissions" | IAM Permission Boundary |
| "Third-party service accessing your resources; prevent confused deputy" | STS ExternalId in trust policy |
Common Traps
- Cognito User Pool vs Identity Pool: User Pool = authentication, JWT tokens. Identity Pool = AWS credentials for direct service access. They work together but serve different purposes.
- SSE-KMS costs: Every GET/PUT on SSE-KMS objects calls KMS. High-throughput buckets can hit KMS throttle limits. Monitor
NumberOfRequestsToKMSmetric. - Lambda Authorizer caching: By default, API Gateway caches the IAM policy returned by the authorizer. A cached policy for a different resource or action could incorrectly grant or deny access. Set cache TTL=0 for sensitive endpoints where every request needs fresh validation.
- Permission Boundary ≠ SCP: Permission Boundary applies to a specific user or role within an account. SCP applies to the entire AWS account (or OU). They both SET CEILINGS, not grant permissions.
- KMS Key Policy is mandatory: Unlike IAM, a KMS key with no key policy is inaccessible to everyone. The default key policy must include the account root ARN.
- Cross-account S3 access: Requires BOTH the S3 bucket policy in Account B allowing Account A AND an IAM policy in Account A allowing the S3 action. One alone is insufficient.
Key Terms — Domain 2
| Term | One-Line Definition |
|---|---|
| Principal | The entity (user, role, service) making an API request |
| Explicit Deny | A DENY statement that overrides any ALLOW — the highest-priority rule |
| Permission Boundary | A policy that caps the maximum permissions a user or role can have |
| SCP (Service Control Policy) | Organization-level policy setting the ceiling for entire AWS accounts |
| Envelope Encryption | Encrypting a Data Encryption Key (DEK) with a master KMS key |
| DEK (Data Encryption Key) | A symmetric key used to encrypt data; itself encrypted by a KMS CMK |
| CMK (Customer Master Key) | A KMS key used to encrypt/decrypt Data Encryption Keys |
| JWT (JSON Web Token) | A signed, base64-encoded token containing user claims; issued by Cognito User Pools |
| SigV4 | AWS's request signing algorithm; proves request authenticity using HMAC-SHA256 |
| Lambda Authorizer | A Lambda function that validates tokens and returns an IAM policy for API Gateway |
| Trust Policy | Defines who can assume an IAM role (the Principal for sts:AssumeRole) |
| ExternalId | A secret value in a role trust policy to prevent the Confused Deputy problem |
| Pre-signed URL | Time-limited S3 URL granting temporary access without AWS credentials |
| WAF Web ACL | Web Application Firewall rule set attached to CloudFront, ALB, or API Gateway |
| Automatic Rotation | Secrets Manager feature that automatically updates a secret using a Lambda function |
End of Domain 2. Continue to Domain 3: Deployment →
Ready to test yourself?
Practice questions for this topic