Ensure CodeBuild Projects Do Not Contain Secrets in Plaintext Environment Variables
Overview
This check ensures that AWS CodeBuild projects do not store sensitive information (API keys, passwords, tokens, credentials) directly in plaintext environment variables. Instead, secrets should be securely stored in AWS Secrets Manager or AWS Systems Manager Parameter Store and referenced by the build project.
Risk
Storing secrets in plaintext environment variables is a critical security risk:
- Exposure in console and CLI: Anyone with access to view the CodeBuild project can see the secret values
- Leaked in build logs: Secrets may accidentally appear in build output or logs
- Version control risk: If project configuration is exported or stored, secrets travel with it
- No rotation: Plaintext values cannot be automatically rotated
- Blast radius: Compromised credentials can lead to unauthorized AWS access, data breaches, and supply-chain attacks
Remediation Steps
Prerequisites
You need:
- Access to the AWS Console with permissions to edit CodeBuild projects
- Permissions to create secrets in Secrets Manager or parameters in Parameter Store
Required IAM permissions
To remediate this issue, you need permissions including:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"codebuild:BatchGetProjects",
"codebuild:UpdateProject"
],
"Resource": "arn:aws:codebuild:us-east-1:<account-id>:project/<project-name>"
},
{
"Effect": "Allow",
"Action": [
"secretsmanager:CreateSecret",
"secretsmanager:PutSecretValue"
],
"Resource": "*"
},
{
"Effect": "Allow",
"Action": [
"ssm:PutParameter"
],
"Resource": "*"
}
]
}
AWS Console Method
Step 1: Create a secret to store your sensitive value
- Go to AWS Secrets Manager in the AWS Console
- Click Store a new secret
- Choose Other type of secret
- Enter your secret as a key-value pair (e.g., key:
password, value:your-secret-value) - Click Next
- Give your secret a name (e.g.,
myapp/db-password) - Complete the wizard and click Store
- Copy the secret ARN - you will need it in the next step
Step 2: Update CodeBuild project to reference the secret
- Go to CodeBuild > Build projects
- Click on the project name that has the plaintext secret
- Click Edit > Environment
- Scroll down to Additional configuration > Environment variables
- Find the variable containing the secret
- Change the Type from
PlaintexttoSecrets Manager - Replace the Value with the secret ARN or secret name (e.g.,
myapp/db-password) - Click Update environment
Step 3: Grant CodeBuild permission to read the secret
- Go to IAM > Roles
- Find and click on the CodeBuild service role (usually named like
codebuild-<project-name>-service-role) - Click Add permissions > Attach policies
- Click Create policy and use this JSON:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "secretsmanager:GetSecretValue",
"Resource": "arn:aws:secretsmanager:us-east-1:<account-id>:secret:myapp/db-password-*"
}
]
}
- Name the policy (e.g.,
CodeBuildSecretsAccess) and attach it to the role
AWS CLI (optional)
Identify projects with plaintext secrets
List all CodeBuild projects:
aws codebuild list-projects --region us-east-1
Inspect a specific project's environment variables:
aws codebuild batch-get-projects \
--names <project-name> \
--region us-east-1 \
--query 'projects[0].environment.environmentVariables'
Look for variables where type is PLAINTEXT and the value appears to be a secret.
Create a secret in Secrets Manager
aws secretsmanager create-secret \
--name "myapp/db-password" \
--description "Database password for CodeBuild project" \
--secret-string "your-secret-value-here" \
--region us-east-1
Update the CodeBuild project
Create a JSON file with the updated environment configuration:
cat > env-update.json << 'EOF'
{
"environmentVariables": [
{
"name": "DB_PASSWORD",
"value": "arn:aws:secretsmanager:us-east-1:123456789012:secret:myapp/db-password",
"type": "SECRETS_MANAGER"
},
{
"name": "BUILD_ENV",
"value": "production",
"type": "PLAINTEXT"
}
]
}
EOF
Update the project:
aws codebuild update-project \
--name <project-name> \
--environment file://env-update.json \
--region us-east-1
Note: The --environment flag requires the full environment configuration. First retrieve the current configuration, modify the environment variables, then update.
Grant CodeBuild access to the secret
Attach an inline policy to the CodeBuild service role:
aws iam put-role-policy \
--role-name codebuild-<project-name>-service-role \
--policy-name SecretsManagerAccess \
--policy-document '{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "secretsmanager:GetSecretValue",
"Resource": "arn:aws:secretsmanager:us-east-1:123456789012:secret:myapp/db-password-*"
}
]
}'
CloudFormation (optional)
This template creates a CodeBuild project that references secrets from Secrets Manager instead of storing them in plaintext:
AWSTemplateFormatVersion: '2010-09-09'
Description: CodeBuild project with secrets stored in Secrets Manager
Parameters:
ProjectName:
Type: String
Default: my-codebuild-project
Resources:
# Secret in Secrets Manager
DatabaseSecret:
Type: AWS::SecretsManager::Secret
Properties:
Name: !Sub '${ProjectName}/db-password'
Description: Database password for CodeBuild project
GenerateSecretString:
SecretStringTemplate: '{}'
GenerateStringKey: password
PasswordLength: 32
ExcludeCharacters: '"@/\'
# CodeBuild Service Role
CodeBuildRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Principal:
Service: codebuild.amazonaws.com
Action: sts:AssumeRole
Policies:
- PolicyName: SecretsManagerAccess
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Action:
- secretsmanager:GetSecretValue
Resource: !Ref DatabaseSecret
- PolicyName: CloudWatchLogsAccess
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Action:
- logs:CreateLogGroup
- logs:CreateLogStream
- logs:PutLogEvents
Resource: '*'
# CodeBuild Project
CodeBuildProject:
Type: AWS::CodeBuild::Project
Properties:
Name: !Ref ProjectName
ServiceRole: !GetAtt CodeBuildRole.Arn
Artifacts:
Type: NO_ARTIFACTS
Environment:
Type: LINUX_CONTAINER
ComputeType: BUILD_GENERAL1_SMALL
Image: aws/codebuild/amazonlinux2-x86_64-standard:4.0
EnvironmentVariables:
# CORRECT: Reference secret from Secrets Manager
- Name: DB_PASSWORD
Type: SECRETS_MANAGER
Value: !Ref DatabaseSecret
# CORRECT: Non-sensitive value as plaintext
- Name: BUILD_ENV
Type: PLAINTEXT
Value: production
Source:
Type: NO_SOURCE
BuildSpec: |
version: 0.2
phases:
build:
commands:
- echo "Build started"
Outputs:
ProjectArn:
Value: !GetAtt CodeBuildProject.Arn
Description: CodeBuild project ARN
Deploy the template:
aws cloudformation deploy \
--template-file template.yaml \
--stack-name codebuild-secure-project \
--capabilities CAPABILITY_IAM \
--region us-east-1
Terraform (optional)
This Terraform configuration creates a CodeBuild project that securely references secrets:
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.0"
}
}
}
provider "aws" {
region = "us-east-1"
}
# Create a secret in Secrets Manager
resource "aws_secretsmanager_secret" "db_password" {
name = "myapp/db-password"
description = "Database password for CodeBuild project"
}
resource "aws_secretsmanager_secret_version" "db_password" {
secret_id = aws_secretsmanager_secret.db_password.id
secret_string = "your-secret-value-here" # In practice, use a variable
}
# CodeBuild Service Role
resource "aws_iam_role" "codebuild" {
name = "codebuild-example-role"
assume_role_policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Action = "sts:AssumeRole"
Effect = "Allow"
Principal = {
Service = "codebuild.amazonaws.com"
}
}
]
})
}
# Policy to allow CodeBuild to read the secret
resource "aws_iam_role_policy" "secrets_access" {
name = "SecretsManagerAccess"
role = aws_iam_role.codebuild.id
policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Effect = "Allow"
Action = "secretsmanager:GetSecretValue"
Resource = aws_secretsmanager_secret.db_password.arn
}
]
})
}
# CodeBuild Project with secrets referenced from Secrets Manager
resource "aws_codebuild_project" "example" {
name = "example-project"
service_role = aws_iam_role.codebuild.arn
artifacts {
type = "NO_ARTIFACTS"
}
environment {
compute_type = "BUILD_GENERAL1_SMALL"
image = "aws/codebuild/amazonlinux2-x86_64-standard:4.0"
type = "LINUX_CONTAINER"
image_pull_credentials_type = "CODEBUILD"
# CORRECT: Reference secret from Secrets Manager
environment_variable {
name = "DB_PASSWORD"
value = aws_secretsmanager_secret.db_password.arn
type = "SECRETS_MANAGER"
}
# CORRECT: Reference parameter from Parameter Store
environment_variable {
name = "API_KEY"
value = "/myapp/api-key"
type = "PARAMETER_STORE"
}
# CORRECT: Non-sensitive value can be plaintext
environment_variable {
name = "BUILD_ENV"
value = "production"
type = "PLAINTEXT"
}
}
source {
type = "NO_SOURCE"
buildspec = <<-BUILDSPEC
version: 0.2
phases:
build:
commands:
- echo "Build started"
BUILDSPEC
}
}
Apply the configuration:
terraform init
terraform plan
terraform apply
Verification
After remediation, verify the fix:
- Go to CodeBuild > Build projects > select your project
- Click Edit > Environment
- Check that all sensitive environment variables show Type as
Secrets ManagerorParameter Store(notPlaintext) - Run a test build to ensure the project can still access the secrets
Verify with AWS CLI
aws codebuild batch-get-projects \
--names <project-name> \
--region us-east-1 \
--query 'projects[0].environment.environmentVariables[*].{Name:name,Type:type}'
Expected output should show SECRETS_MANAGER or PARAMETER_STORE for sensitive variables:
[
{
"Name": "DB_PASSWORD",
"Type": "SECRETS_MANAGER"
},
{
"Name": "BUILD_ENV",
"Type": "PLAINTEXT"
}
]
Verify with Prowler
Re-run the Prowler check to confirm remediation:
prowler aws --checks codebuild_project_no_secrets_in_variables --region us-east-1
The check should now pass for your remediated project.
Additional Resources
- AWS CodeBuild Environment Variables
- Using Secrets Manager with CodeBuild
- AWS Secrets Manager Best Practices
- AWS Systems Manager Parameter Store
Notes
- Secrets Manager vs Parameter Store: Use Secrets Manager for credentials that need automatic rotation. Use Parameter Store for simpler secrets or configuration values.
- Secret format: When referencing Secrets Manager secrets, you can use either the secret ARN or secret name. For secrets with JSON values, use the format
secret-name:json-keyto extract specific fields. - Build failures: After updating to use Secrets Manager, ensure the CodeBuild service role has
secretsmanager:GetSecretValuepermission, or builds will fail. - Cost consideration: Secrets Manager charges per secret per month. Parameter Store SecureString parameters are free for standard throughput.
- Existing builds: This change does not affect builds already in progress. New builds will use the updated configuration.