Tarek Cheikh
Founder & AWS Cloud Architect
Part 3 of 4 in the Lambda Security Series
Part 1 described the risks. Part 2 introduced lambda-security-scanner and the nineteen checks it runs against every function. This part closes the loop. A finding is only useful if it leads somewhere, and it usually needs to lead to two places: a compliance control that an auditor cares about, and a command that an engineer can run to make the finding go away. The scanner produces both.
Compliance frameworks were mostly written before serverless existed, so people assume they do not apply. They do. The controls are about outcomes, not implementation. "Restrict network access between trusted and untrusted zones," "authenticate access to system components," "enforce least privilege," and "retain audit logs" are all requirements that a Lambda function either meets or violates, regardless of the fact that there is no server to point at.
The scanner evaluates each function against ten frameworks and reports which controls pass and which fail, per function. In total it maps to 81 controls:
| Framework | Controls | Focus |
|---|---|---|
| AWS Foundational Security Best Practices | 5 | Lambda-specific Security Hub controls |
| CIS AWS Compute Services Benchmark | 8 | Compute service hardening |
| PCI DSS v4.0.1 | 8 | Payment card data protection |
| HIPAA Security Rule | 9 | Healthcare data security |
| SOC 2 | 11 | Service organization controls |
| ISO 27001:2022 | 11 | Information security management |
| ISO 27017:2015 | 4 | Cloud-specific security controls |
| ISO 27018:2019 | 5 | Protection of PII in the cloud |
| GDPR | 8 | EU data protection |
| NIST SP 800-53 Rev5 | 12 | Federal security controls |
One careful note on the control identifiers, because credibility depends on it. Most frameworks use their real citations: HIPAA 164.312(a)(1), ISO 27001 A.5.15, SOC 2 CC6.1, NIST AC-3, and so on. The CIS entries are different. They map to the genuine CIS AWS Compute Services Benchmark guidance for Lambda, but the CIS-Lambda.N identifiers are the scanner's own labels, not the benchmark's official recommendation numbers, which live under section 5. They are an alignment aid, not a verbatim citation. The scanner says so in its own documentation, and so do I.
The mapping is many-to-many. One misconfiguration usually breaks several controls at once, which is exactly why these findings matter to an audit.
Take a public function URL with AuthType: NONE. That single finding fails an authentication control in PCI DSS, an access-restriction control in SOC 2, an access-control requirement in NIST 800-53, and the corresponding AWS FSBP Lambda control, all from one misconfiguration. Fix the one thing and several controls flip to passing together. The same is true in reverse for plaintext secrets, which touch data-protection requirements across PCI DSS, HIPAA, ISO 27018, and GDPR simultaneously.
If you only need the posture and not the full security scan, run the compliance report on its own:
lambda-security-scanner security --compliance-only
That produces a per-function, per-framework breakdown of passed and failed controls, written as a JSON report on every run. It is the artifact to hand to an auditor or attach to a control review.
The rest of this article is the remediation playbook: one fix per check, with the AWS CLI command to apply it. Run these against your own functions after a scan tells you which ones need them.
aws lambda update-function-configuration \
--function-name my-function \
--runtime python3.13
As of May 2026, examples of current supported runtimes are nodejs24.x, nodejs22.x, python3.14, python3.13, java25, java21, dotnet10, dotnet8, ruby4.0, ruby3.4, and provided.al2023. Update before the runtime's block-update date arrives, not after, because a blocked runtime can no longer be updated in place and forces a more disruptive migration.
aws lambda update-function-configuration \
--function-name my-function \
--timeout 30
Set the timeout to what the function actually needs. A 900-second timeout on a function that finishes in three seconds means a stuck or abused invocation can burn compute for fifteen minutes before Lambda stops it.
# Store the secret in Secrets Manager
aws secretsmanager create-secret \
--name my-function/db-password \
--secret-string "MyPr0ductionP@ss!"
# Replace the plaintext value with a reference
aws lambda update-function-configuration \
--function-name my-function \
--environment '{"Variables":{"DB_SECRET_ARN":"arn:aws:secretsmanager:us-east-1:123456789012:secret:my-function/db-password-AbCdEf"}}'
Then resolve the secret at runtime with secretsmanager:GetSecretValue. The environment variable now holds a pointer, which is exactly the pattern the scanner treats as clean.
aws lambda update-function-configuration \
--function-name my-function \
--ephemeral-storage '{"Size": 512}'
The default is 512 MB. If the function does not need more, do not allocate more. Larger scratch space increases what a compromised function can stage locally.
aws lambda get-function-configuration \
--function-name my-function \
--query "Layers[*].Arn"
Any layer owned by an account that is not yours runs arbitrary code inside your function with your function's permissions. Confirm every external layer comes from a source you trust.
aws lambda update-function-configuration \
--function-name my-function \
--tracing-config Mode=Active
aws sqs create-queue --queue-name my-function-dlq
aws lambda update-function-configuration \
--function-name my-function \
--dead-letter-config TargetArn=arn:aws:sqs:us-east-1:123456789012:my-function-dlq
# Remove the wildcard statement
aws lambda remove-permission \
--function-name my-function \
--statement-id public-access
# Add a scoped permission instead
aws lambda add-permission \
--function-name my-function \
--statement-id api-gateway-invoke \
--action lambda:InvokeFunction \
--principal apigateway.amazonaws.com \
--source-arn arn:aws:execute-api:us-east-1:123456789012:myapi/*/GET/resource
# Require signed requests
aws lambda update-function-url-config \
--function-name my-function \
--auth-type AWS_IAM
# Or remove the function URL entirely
aws lambda delete-function-url-config \
--function-name my-function
aws lambda update-function-url-config \
--function-name my-function \
--cors '{"AllowOrigins":["https://myapp.example.com"]}'
# Detach the overprivileged managed policy
aws iam detach-role-policy \
--role-name my-function-role \
--policy-arn arn:aws:iam::aws:policy/AdministratorAccess
# Attach a least-privilege policy
aws iam attach-role-policy \
--role-name my-function-role \
--policy-arn arn:aws:iam::123456789012:policy/my-function-least-privilege
Use IAM Access Analyzer to generate a least-privilege policy from the function's actual CloudTrail activity, rather than guessing at the permission set.
Each function should assume a role scoped to only what that function needs. Sharing one role across functions means a compromise of the weakest function inherits the access of all the others.
aws lambda update-function-configuration \
--function-name my-function \
--vpc-config SubnetIds=subnet-abc123,subnet-def456,SecurityGroupIds=sg-12345678
Not every function needs a VPC. Functions that reach databases, internal APIs, or other private resources do.
aws lambda update-function-configuration \
--function-name my-function \
--vpc-config SubnetIds=subnet-abc123,subnet-def456,SecurityGroupIds=sg-12345678
Provide subnets in at least two AZs. A single-AZ deployment goes down with that one AZ.
aws ec2 revoke-security-group-egress \
--group-id sg-12345678 \
--ip-permissions '[{"IpProtocol": "-1", "IpRanges": [{"CidrIp": "0.0.0.0/0"}]}]'
aws ec2 authorize-security-group-egress \
--group-id sg-12345678 \
--ip-permissions '[{"IpProtocol": "tcp", "FromPort": 443, "ToPort": 443, "IpRanges": [{"CidrIp": "10.0.0.0/8"}]}]'
Replace allow-all outbound with the specific destinations the function needs. Unrestricted egress is how a compromised function exfiltrates data to anywhere.
aws logs put-retention-policy \
--log-group-name /aws/lambda/my-function \
--retention-in-days 90
aws lambda put-function-concurrency \
--function-name my-function \
--reserved-concurrent-executions 100
This matters most for functions that are reachable from outside. Without a concurrency cap, an attacker who can invoke the function can invoke it without limit.
aws signer put-signing-profile \
--profile-name my-signing-profile \
--platform-id AWSLambda-SHA384-ECDSA
aws lambda create-code-signing-config \
--allowed-publishers SigningProfileVersionArns=arn:aws:signer:us-east-1:123456789012:/signing-profiles/my-signing-profile \
--code-signing-policies UntrustedArtifactOnDeployment=Enforce
aws lambda put-function-code-signing-config \
--function-name my-function \
--code-signing-config-arn arn:aws:lambda:us-east-1:123456789012:code-signing-config:csc-abc123
Set the policy to Enforce, not Warn. Warn logs an untrusted artifact and deploys it anyway.
aws lambda update-event-source-mapping \
--uuid my-esm-uuid \
--destination-config '{"OnFailure":{"Destination":"arn:aws:sqs:us-east-1:123456789012:my-function-esm-dlq"}}'
If you fix everything in score order, you fix the right things first. The scanner's deductions already encode the priority:
Part 1 was the problem: serverless moved the attack surface closer to your code, and the misconfigurations are invisible until they are exploited. Part 2 was the tool: nineteen read-only checks, a score per function, and a clear list of what is wrong. This part was the payoff: every finding mapped to the compliance controls that care about it, and a command to fix each one.
That covers the configuration layer completely, which is what lambda-security-scanner checks and what most teams get wrong first. But there is one layer no posture scanner reaches: the code that runs inside the function, the dependencies it ships, and the credentials it holds at runtime. A clean configuration scan and a vulnerable function are entirely compatible. Part 4 covers that application layer, and it is the difference between a competent Lambda security posture and a complete one.
The project is open source under the MIT license:
This article is just the start. Get the full picture with our free whitepaper - 8 chapters covering IAM, S3, VPC, monitoring, agentic AI security, compliance, and a prioritized action plan with 50+ CLI commands.
Toc Consulting: AWS Security & Cloud Architecture
Our team helps engineering teams secure and architect AWS the right way: assessment in week one, a prioritized action plan in week two.
Part 4 of 4 in the Lambda Security Series. The half no posture scanner reaches: event-data injection, stealable execution-role credentials, insecure deserialization, dependency and code scanning, runtime secrets, and detection.
Spin up a local AWS, plant deliberately insecure resources, and run real security scanners against it. No account, no token, no cost, no risk.
Part 2 of 4 in the Lambda Security Series. A deep look at lambda-security-scanner: 19 read-only checks across configuration, access control, network, logging, and supply chain, scored 0 to 100 and mapped to ten compliance frameworks.