Skip to main content

Lambda Function Code Contains No Hardcoded Secrets

Overview

This check scans AWS Lambda function code for hardcoded secrets such as API keys, passwords, tokens, database credentials, and connection strings. Prowler examines all files in your Lambda deployment package and reports specific file names and line numbers where potential secrets are detected.

Risk

Hardcoded secrets in Lambda function code create serious security vulnerabilities:

  • Credential exposure: If your code repository, deployment package, or Lambda layers are accessed by unauthorized parties, attackers can steal and reuse your credentials
  • Data breach potential: Stolen credentials enable access to databases, APIs, and other cloud resources, leading to data exfiltration
  • Difficult rotation: Hardcoded secrets are harder to rotate, extending the window of exposure during a security incident
  • Compliance violations: Many regulatory frameworks (PCI-DSS, HIPAA, SOC 2) prohibit storing secrets in code

Remediation Steps

Prerequisites

  • AWS account access with permissions to modify Lambda functions and create secrets
  • Ability to update your Lambda function code (deployment package or source repository)
Required IAM permissions

You will need the following permissions:

  • lambda:GetFunction - Read function configuration
  • lambda:UpdateFunctionCode - Deploy updated code
  • lambda:UpdateFunctionConfiguration - Modify function settings
  • secretsmanager:CreateSecret - Create new secrets
  • secretsmanager:GetSecretValue - Retrieve secret values
  • iam:PutRolePolicy or iam:AttachRolePolicy - Grant Lambda access to secrets

AWS Console Method

Step 1: Identify the hardcoded secrets

  1. Sign in to the AWS Management Console
  2. Navigate to Lambda > Functions
  3. Select the flagged function
  4. Review the Prowler finding to identify which files and line numbers contain secrets

Step 2: Store secrets in AWS Secrets Manager

  1. Navigate to Secrets Manager in the AWS Console
  2. Click Store a new secret
  3. For Secret type, choose the appropriate option:
    • Credentials for Amazon RDS database (for database passwords)
    • Other type of secret (for API keys, tokens, etc.)
  4. Enter your secret key-value pairs (e.g., api_key = your-actual-api-key)
  5. Click Next
  6. For Secret name, enter a descriptive name (e.g., myapp/prod/api-credentials)
  7. Optionally, add a description and tags
  8. Click Next, configure rotation if desired, then click Store
  9. Copy the Secret ARN for the next step

Step 3: Grant Lambda permission to access the secret

  1. Navigate to IAM > Roles
  2. Find and select your Lambda function's execution role (visible in Lambda function configuration)
  3. Click Add permissions > Create inline policy
  4. Choose the JSON tab and paste:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "secretsmanager:GetSecretValue",
"Resource": "<your-secret-arn>"
}
]
}
  1. Replace <your-secret-arn> with the ARN from Step 2
  2. Click Next, name the policy (e.g., SecretsManagerAccess), and click Create policy

Step 4: Update your Lambda function code

Remove the hardcoded secrets and retrieve them from Secrets Manager at runtime. Here are examples for common languages:

Python example
import boto3
import json

def get_secret():
client = boto3.client('secretsmanager', region_name='us-east-1')
response = client.get_secret_value(SecretId='myapp/prod/api-credentials')
return json.loads(response['SecretString'])

def lambda_handler(event, context):
secrets = get_secret()
api_key = secrets['api_key']
# Use api_key in your code...
Node.js example
const { SecretsManagerClient, GetSecretValueCommand } = require('@aws-sdk/client-secrets-manager');

const client = new SecretsManagerClient({ region: 'us-east-1' });

async function getSecret() {
const command = new GetSecretValueCommand({ SecretId: 'myapp/prod/api-credentials' });
const response = await client.send(command);
return JSON.parse(response.SecretString);
}

exports.handler = async (event) => {
const secrets = await getSecret();
const apiKey = secrets.api_key;
// Use apiKey in your code...
};

Step 5: Deploy the updated function

  1. Package your updated code and upload it via the Lambda console, or
  2. Push to your source repository and trigger your CI/CD pipeline
AWS CLI (optional)

Create a secret:

aws secretsmanager create-secret \
--name myapp/prod/api-credentials \
--description "API credentials for my Lambda function" \
--secret-string '{"api_key":"your-api-key","db_password":"your-db-password"}' \
--region us-east-1

Grant Lambda access to the secret:

First, identify your Lambda function's execution role:

aws lambda get-function \
--function-name <your-function-name> \
--region us-east-1 \
--query 'Configuration.Role' \
--output text

Then add an inline policy to that role:

aws iam put-role-policy \
--role-name <your-lambda-role-name> \
--policy-name SecretsManagerAccess \
--policy-document '{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Action": "secretsmanager:GetSecretValue",
"Resource": "arn:aws:secretsmanager:us-east-1:<account-id>:secret:myapp/prod/api-credentials-*"
}]
}'

Update your Lambda function code and deploy:

# Package your updated code
zip -r function.zip .

# Deploy to Lambda
aws lambda update-function-code \
--function-name <your-function-name> \
--zip-file fileb://function.zip \
--region us-east-1
CloudFormation (optional)
AWSTemplateFormatVersion: '2010-09-09'
Description: Lambda function with Secrets Manager integration

Parameters:
ApiKeyValue:
Type: String
NoEcho: true
Description: The API key to store in Secrets Manager

Resources:
ApiSecret:
Type: AWS::SecretsManager::Secret
Properties:
Name: myapp/prod/api-credentials
Description: API credentials for Lambda function
SecretString: !Sub '{"api_key":"${ApiKeyValue}"}'

LambdaExecutionRole:
Type: AWS::IAM::Role
Properties:
RoleName: MyLambdaExecutionRole
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Principal:
Service: lambda.amazonaws.com
Action: sts:AssumeRole
ManagedPolicyArns:
- arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
Policies:
- PolicyName: SecretsManagerAccess
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Action: secretsmanager:GetSecretValue
Resource: !Ref ApiSecret

MyLambdaFunction:
Type: AWS::Lambda::Function
Properties:
FunctionName: my-secure-function
Runtime: python3.12
Handler: index.lambda_handler
Role: !GetAtt LambdaExecutionRole.Arn
Environment:
Variables:
SECRET_NAME: !Ref ApiSecret
Code:
ZipFile: |
import boto3
import json
import os

def lambda_handler(event, context):
client = boto3.client('secretsmanager')
secret_name = os.environ['SECRET_NAME']
response = client.get_secret_value(SecretId=secret_name)
secrets = json.loads(response['SecretString'])
# Use secrets in your code
return {'statusCode': 200}

Outputs:
SecretArn:
Description: ARN of the created secret
Value: !Ref ApiSecret
FunctionArn:
Description: ARN of the Lambda function
Value: !GetAtt MyLambdaFunction.Arn

Deploy the template:

aws cloudformation deploy \
--template-file lambda-secrets.yaml \
--stack-name lambda-secrets-stack \
--parameter-overrides ApiKeyValue=your-actual-api-key \
--capabilities CAPABILITY_NAMED_IAM \
--region us-east-1
Terraform (optional)
provider "aws" {
region = "us-east-1"
}

# Store the secret in Secrets Manager
resource "aws_secretsmanager_secret" "api_credentials" {
name = "myapp/prod/api-credentials"
description = "API credentials for Lambda function"
}

resource "aws_secretsmanager_secret_version" "api_credentials" {
secret_id = aws_secretsmanager_secret.api_credentials.id
secret_string = jsonencode({
api_key = var.api_key
db_password = var.db_password
})
}

# IAM role for Lambda
resource "aws_iam_role" "lambda_role" {
name = "my-lambda-execution-role"

assume_role_policy = jsonencode({
Version = "2012-10-17"
Statement = [{
Action = "sts:AssumeRole"
Effect = "Allow"
Principal = {
Service = "lambda.amazonaws.com"
}
}]
})
}

# Attach basic execution policy
resource "aws_iam_role_policy_attachment" "lambda_basic" {
role = aws_iam_role.lambda_role.name
policy_arn = "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
}

# Grant access to the specific secret
resource "aws_iam_role_policy" "secrets_access" {
name = "SecretsManagerAccess"
role = aws_iam_role.lambda_role.id

policy = jsonencode({
Version = "2012-10-17"
Statement = [{
Effect = "Allow"
Action = "secretsmanager:GetSecretValue"
Resource = aws_secretsmanager_secret.api_credentials.arn
}]
})
}

# Lambda function
resource "aws_lambda_function" "my_function" {
filename = "function.zip"
function_name = "my-secure-function"
role = aws_iam_role.lambda_role.arn
handler = "index.lambda_handler"
runtime = "python3.12"
source_code_hash = filebase64sha256("function.zip")

environment {
variables = {
SECRET_NAME = aws_secretsmanager_secret.api_credentials.name
}
}
}

# Variables
variable "api_key" {
type = string
sensitive = true
description = "API key to store in Secrets Manager"
}

variable "db_password" {
type = string
sensitive = true
description = "Database password to store in Secrets Manager"
}

Verification

After completing the remediation:

  1. Navigate to Lambda > Functions > your function in the AWS Console
  2. Go to the Code tab and verify no hardcoded secrets remain
  3. Go to the Configuration > Permissions tab and confirm the execution role has Secrets Manager access
  4. Test your function to ensure it retrieves secrets correctly
  5. Re-run the Prowler check to confirm the issue is resolved
CLI verification commands

Verify the secret exists:

aws secretsmanager describe-secret \
--secret-id myapp/prod/api-credentials \
--region us-east-1

Verify Lambda role has Secrets Manager permissions:

aws iam list-role-policies \
--role-name <your-lambda-role-name>

Test the Lambda function:

aws lambda invoke \
--function-name <your-function-name> \
--region us-east-1 \
output.json

cat output.json

Additional Resources

Notes

  • Alternative: AWS Systems Manager Parameter Store: For simpler secrets or cost savings, you can use Parameter Store with SecureString parameters instead of Secrets Manager
  • Caching for performance: Consider using the AWS Parameters and Secrets Lambda Extension to cache secrets and reduce API calls
  • Secret rotation: Enable automatic rotation for database credentials and other supported secret types
  • CI/CD integration: Add secret scanning tools (like git-secrets, truffleHog, or Gitleaks) to your CI/CD pipeline to prevent secrets from being committed
  • Environment variables: Never store secrets in Lambda environment variables without encryption; they are visible in the console and API responses
  • Logging caution: Ensure your code does not log secret values; mask or redact sensitive data in logs