AWS Lambda Security Hardening: IAM Roles, VPC Configuration, Secrets, and Runtime Controls

Retool's new app builder is where AI-generated code ships safely
Building apps with AI is easy. Getting them to production safely is another story.
Lambda's security model differs from EC2 in ways that make the familiar EC2 security checklist incomplete. Lambda has no persistent OS to harden, no SSH surface to secure, and no long-lived session to monitor — but it has execution roles that can grant broad IAM access, environment variables that expose secrets to anyone with IAM read access, and network configurations that determine whether a compromise can reach the internet for exfiltration or command-and-control.
The security configuration surface for a Lambda function is smaller than for an EC2 instance but requires the same deliberate attention. IAM least privilege, secrets management, and network controls are the three areas that account for the majority of Lambda security risk.
IAM execution roles: the highest-priority Lambda security control
The execution role is the first security question to ask about any Lambda function because it determines what damage is possible if the function is compromised. A function with an Administrator role grants full account access to any code injection vulnerability in the handler; a function with a role scoped to one DynamoDB table and one S3 prefix limits the blast radius to exactly those resources. The most common over-permission patterns are using AWS-managed full-access policies (AmazonDynamoDBFullAccess, AmazonS3FullAccess) when only a subset of actions on specific resources is needed, and sharing a single execution role across multiple Lambda functions so the role accumulates the union of all permissions any function in the group requires. The remediation is to enumerate the exact AWS API calls each function makes via CloudTrail, then build a resource-specific IAM policy covering only those actions on those ARNs.
Audit existing execution roles for the most common over-permission patterns
The most common Lambda IAM over-permission patterns: (1) Using AWS managed policies that grant full service access (AmazonDynamoDBFullAccess, AmazonS3FullAccess) when the function only needs access to specific resources within that service. (2) Attaching a role that was created for another purpose and has accumulated permissions from multiple use cases. (3) Using s3:* on * ARN (all S3 actions on all buckets) when the function only writes to one bucket prefix. Remediation: for each overly permissive role, enumerate the specific API calls the function actually makes (check CloudTrail for the function's IAM ARN: eventSource matching the services used, grouped by eventName), then build a minimal policy covering only those specific API calls on those specific resources.
Use one execution role per function, never shared execution roles
A shared execution role between multiple Lambda functions means that every function in the group has the union of all permissions any function in the group needs. A function that only needs DynamoDB access and shares a role with a function that also needs SES email sending can now send emails — which is useful for spam and phishing from your domain if the first function is compromised. Create a separate IAM role for each Lambda function. The naming convention 'lambda-FUNCTION_NAME-execution-role' makes the relationship explicit in the IAM console. Terraform, CDK, and SAM all support per-function role creation as part of the function resource definition — the incremental effort is a few lines of IAM policy code per function.
Secrets management: eliminate plaintext credentials from Lambda
Environment variables are the most common secrets storage location in Lambda and the most visible to attackers who gain IAM ReadOnly access, because the lambda:GetFunctionConfiguration permission returns all environment variable values in plaintext. Any IAM user or role granted ReadOnlyAccess to the AWS account can read every secret stored as a Lambda environment variable across all functions in the account. AWS Secrets Manager and SSM Parameter Store both provide encrypted, access-controlled secret storage where access is governed by IAM policies on the secret itself rather than the function configuration. Migrating from environment variables to Secrets Manager requires updating the function code to call the Secrets Manager API at startup and caching the result in the function global scope, which adds a one-time cold-start latency of 50-100 milliseconds.
Audit all Lambda functions for environment variable secrets in one scan
Run a one-time audit across all Lambda functions to identify functions with credentials stored in environment variables: aws lambda list-functions --query 'Functions[*].FunctionName' --output text | xargs -I {} aws lambda get-function-configuration --function-name {} --query '[FunctionName, Environment.Variables]' --output json > lambda-env-vars.json. Review the output for any variable names containing 'password', 'secret', 'api_key', 'token', 'db_pass', or 'credentials.' Any matching variable should be migrated to Secrets Manager or SSM Parameter Store. Prioritize functions with database credentials and third-party API keys first — these represent the highest impact if exposed.
Briefings like this, every morning before 9am.
Threat intel, active CVEs, and campaign alerts, distilled for practitioners. 50,000+ subscribers. No noise.
The bottom line
AWS Lambda security hardening has three priorities that account for most of the risk surface: least-privilege IAM execution roles (a compromised function with minimal permissions has minimal blast radius), secrets management via Secrets Manager instead of environment variables (eliminates credential exposure via IAM ReadOnly access), and VPC configuration for functions that access internal resources (controls outbound traffic and prevents internet-based exfiltration). Audit all existing functions for these three controls using the AWS CLI queries in this guide, prioritize remediation by function sensitivity (functions that access production databases or process PII first), and build the controls into your Lambda deployment templates so new functions are born with the correct configuration.
Frequently asked questions
How do I create a least-privilege IAM execution role for an AWS Lambda function?
Lambda execution role least-privilege design: (1) Start from no permissions and add only what the function needs — do not use managed policies like AmazonDynamoDBFullAccess when the function only needs access to specific resources within that service. (2) Identify all AWS API calls the function makes: review the function code to find every boto3/AWS SDK call. List the service, action, and specific resource (if applicable). (3) Build the IAM policy with specific actions and ARNs: example for a function that reads from one DynamoDB table and writes to one S3 bucket: {Version:'2012-10-17', Statement:[{Effect:'Allow', Action:['dynamodb:GetItem','dynamodb:Query'], Resource:'arn:aws:dynamodb:us-east-1:ACCOUNT:table/specific-table-name'},{Effect:'Allow', Action:['s3:PutObject'], Resource:'arn:aws:s3:::specific-bucket-name/specific-prefix/*'}]}. (4) Add the basic execution role permissions (for logging to CloudWatch): {Effect:'Allow', Action:['logs:CreateLogGroup','logs:CreateLogStream','logs:PutLogEvents'], Resource:'arn:aws:logs:*:ACCOUNT:*'}. If the function is in a VPC, add ec2:CreateNetworkInterface, ec2:DescribeNetworkInterfaces, ec2:DeleteNetworkInterface to the logging permissions. (5) Use IAM Access Analyzer to validate the policy does not grant access beyond the intended resource scope.
How do I replace environment variable secrets with AWS Secrets Manager in a Lambda function?
Secrets Manager integration for Lambda: (1) Store the secret in Secrets Manager: aws secretsmanager create-secret --name /production/myapp/db-password --secret-string 'your-password'. (2) Grant the Lambda execution role permission to retrieve the specific secret: {Effect:'Allow', Action:['secretsmanager:GetSecretValue'], Resource:'arn:aws:secretsmanager:us-east-1:ACCOUNT:secret:/production/myapp/db-password-*'} — use the exact ARN with the trailing * to account for the Secrets Manager version suffix. (3) Remove the environment variable from the Lambda function configuration. (4) Update the function code to retrieve the secret at runtime: Python example: import boto3, json; client = boto3.client('secretsmanager'); response = client.get_secret_value(SecretId='/production/myapp/db-password'); secret = response['SecretString']. (5) Cache the secret in the function's global scope (outside the handler function) to avoid API calls on every invocation: global DB_PASSWORD; if not DB_PASSWORD: DB_PASSWORD = get_secret(). (6) Performance: Secrets Manager retrieval adds 50-100ms latency on cold starts when the cache is empty. Lambda Extensions (AWS Lambda Powertools Secrets Manager extension) can pre-load secrets before the function handler runs. SSM Parameter Store is an alternative: GetParameter with SecureString is cheaper than Secrets Manager but lacks automatic rotation support.
Should my Lambda function run inside a VPC, and what are the tradeoffs?
Lambda VPC configuration tradeoffs: Without VPC (default): Lambda functions run in AWS-managed infrastructure with a randomly assigned public IP. The function can reach any internet endpoint directly. Pros: no configuration required, no cold start penalty from VPC ENI setup (this penalty was largely eliminated in 2019 with the Hyperplane VPC improvements). Cons: the function can exfiltrate data or communicate with command-and-control servers on the internet — you have no network-level control over outbound traffic. With VPC: Lambda functions run in your VPC subnets and use your Security Groups. You control outbound traffic via Security Group rules and route table configuration. Pros: outbound traffic control (the function can only reach what your Security Groups and route tables permit), access to VPC resources (RDS databases, ElastiCache, internal services on private subnets). Cons: requires subnet configuration and ENI management, functions in private subnets need a NAT Gateway to reach the internet (adds cost). When to use VPC: always, for functions that access databases or internal services (eliminates the need to expose those resources to the internet). For functions that only call external APIs: VPC is optional but recommended for environments with outbound traffic control requirements.
How do I configure Lambda Function URL security to prevent unauthorized access?
Lambda Function URL security configuration: (1) AuthType AWS_IAM: the function URL requires a SigV4 signed request, meaning only AWS principals with the lambda:InvokeFunctionUrl permission on the function can call the URL. This is the secure option for internal or service-to-service use. (2) AuthType NONE: the URL is publicly accessible to anyone who knows the URL. Appropriate only for genuinely public-facing functions that do not require authentication (public webhooks, static API endpoints). (3) Restrict the CORS configuration: if your Function URL is called from a web browser, configure CORS to only allow requests from your specific domain: AllowOrigins: [https://app.your-company.com]. Do not use AllowOrigins: ['*'] for sensitive functions. (4) Add an application-level authentication check in the function code for NONE auth URLs that still need some protection: validate a secret token in the request headers (X-API-Key or Authorization Bearer) before processing the request. (5) Monitor Function URL invocations: Lambda logs all invocations to CloudWatch — set up a CloudWatch metric filter and alarm for function error rates or unusual invocation volumes that may indicate abuse.
How do I audit existing Lambda functions for security misconfigurations?
Lambda security audit process: (1) List all functions and their execution roles: aws lambda list-functions --query 'Functions[*].[FunctionName,Role]' --output table. For each function, evaluate whether the attached role is least-privilege or uses broad managed policies. Flag functions with AdministratorAccess, PowerUserAccess, or AmazonEC2FullAccess in their execution role. (2) Check for secrets in environment variables: aws lambda get-function-configuration --function-name FUNCTION_NAME --query 'Environment.Variables'. Look for any variables containing 'password', 'secret', 'key', 'token' — these are credential storage indicators. (3) Check Function URL configurations: aws lambda list-function-url-configs --function-name FUNCTION_NAME. Any result with AuthType=NONE should be investigated. (4) Check VPC configuration: aws lambda get-function-configuration --function-name FUNCTION_NAME --query 'VpcConfig'. Functions with an empty VpcConfig have no VPC — evaluate whether outbound internet access from the function is appropriate. (5) Check for code signing: aws lambda get-function-code-signing-config --function-name FUNCTION_NAME. Functions without code signing can deploy unsigned, unverified code. (6) Use AWS Security Hub Lambda controls: Security Hub includes automated checks for Lambda security best practices including lambda-function-public-access-prohibited, lambda-inside-vpc, and others.
How do I implement Lambda code signing to prevent deployment of unauthorized function code?
Lambda code signing prevents deployment of Lambda function code that has not been signed by an approved key, reducing the risk of supply chain compromise where an attacker modifies function code during the deployment pipeline. Setup: (1) Create an AWS Signer signing profile: aws signer put-signing-profile --profile-name LambdaCodeSigningProfile --platform-id AWSLambda-SHA384-ECDSA. (2) Create a code signing configuration: aws lambda create-code-signing-config --allowed-publishers SigningProfileVersionArns=arn:aws:signer:REGION:ACCOUNT:/signing-profiles/LambdaCodeSigningProfile --code-signing-policies UntrustedArtifactOnDeployment=Enforce. (3) Associate the signing config with functions: aws lambda update-function-code-signing-config --function-name MY_FUNCTION --code-signing-config-arn arn:aws:lambda:REGION:ACCOUNT:code-signing-config:csc-xxx. (4) Update the CI/CD pipeline to sign deployment packages: aws signer start-signing-job before deployment. With code signing enforced, any attempt to deploy an unsigned or incorrectly signed Lambda package fails, preventing an attacker who compromises the CI/CD pipeline from deploying malicious code to production Lambda functions.
How do I detect and investigate Lambda security incidents using CloudTrail?
Lambda CloudTrail detections: Lambda invocations are logged to CloudTrail as management events. Key detection queries: (1) Unauthorized invocation attempts: eventSource=lambda.amazonaws.com AND errorCode='AccessDenied' AND eventName='InvokeFunction' — alerts when a principal without invoke permission attempts to call a function (may indicate attempted abuse of an exposed Function URL). (2) IAM role policy changes affecting Lambda execution roles: eventSource=iam.amazonaws.com AND eventName IN (AttachRolePolicy, PutRolePolicy) WHERE requestParameters.roleName matches Lambda execution role name. (3) Function code update from an unexpected principal: eventSource=lambda.amazonaws.com AND eventName=UpdateFunctionCode WHERE userIdentity.arn does not match expected deployment pipeline roles — detects unauthorized code modification. (4) Environment variable modification: eventSource=lambda.amazonaws.com AND eventName=UpdateFunctionConfiguration — detects changes to function configuration including environment variable modification, which could be used to inject secrets-stealing code. (5) Function URL creation with NONE auth: eventSource=lambda.amazonaws.com AND eventName=CreateFunctionUrlConfig WHERE requestParameters.authType=NONE.
Sources & references
Free resources
Critical CVE Reference Card 2025–2026
25 actively exploited vulnerabilities with CVSS scores, exploit status, and patch availability. Print it, pin it, share it with your SOC team.
Ransomware Incident Response Playbook
Step-by-step 24-hour IR checklist covering detection, containment, eradication, and recovery. Built for SOC teams, IR leads, and CISOs.
Get threat intel before your inbox does.
50,000+ security professionals read Decryption Digest for early warnings on zero-days, ransomware, and nation-state campaigns. Free, daily, no spam.
Unsubscribe anytime. We never sell your data.

Founder & Cybersecurity Evangelist, Decryption Digest
Cybersecurity professional with expertise in threat intelligence, vulnerability research, and enterprise security. Covers zero-days, ransomware, and nation-state operations for 50,000+ security professionals every morning.
Win a $2,495 Black Hat pass.
Full-access to Black Hat USA 2026 in Las Vegas. Subscribe free to enter.
