Ensure Secrets Manager Secrets Are Not Publicly Accessible
Overview
This check verifies that AWS Secrets Manager secrets do not have resource policies that grant public access. A secret is considered publicly accessible when its resource-based policy uses wildcards (like Principal: "*") without proper conditions to restrict access.
Secrets Manager stores sensitive information such as database credentials, API keys, and certificates. Public access to these secrets defeats their purpose entirely and creates serious security vulnerabilities.
Risk
If a secret is publicly accessible:
- Credential exposure: Anyone on the internet could retrieve your passwords, API keys, or certificates
- Data breaches: Exposed database credentials enable unauthorized access to your data
- Lateral movement: Attackers can use compromised credentials to access other systems
- Compliance violations: Public secrets violate most security frameworks (PCI-DSS, SOC 2, HIPAA, etc.)
Severity: High - This finding should be addressed immediately.
Remediation Steps
Prerequisites
You need permission to view and modify Secrets Manager resource policies. Specifically, you need these IAM permissions:
secretsmanager:GetResourcePolicysecretsmanager:PutResourcePolicysecretsmanager:DeleteResourcePolicy
AWS Console Method
-
Open the AWS Secrets Manager console
-
Click on the secret name that was flagged by Prowler
-
Scroll down to the Resource permissions section
-
Review the current policy. Look for these problematic patterns:
"Principal": "*"without conditions"Principal": {"AWS": "*"}without conditions
-
Click Edit permissions to modify the policy
-
Either:
- Remove the policy entirely (click Delete) if no cross-account access is needed
- Replace wildcards with specific principals - change
"*"to specific IAM ARNs like"arn:aws:iam::123456789012:role/MyRole" - Add conditions to restrict access even with wildcards
-
Enable Block public access by checking the box (this prevents future public policies)
-
Click Save
AWS CLI (optional)
View the current resource policy:
aws secretsmanager get-resource-policy \
--secret-id <your-secret-name> \
--region us-east-1
Option 1: Remove the resource policy entirely
If you do not need cross-account access:
aws secretsmanager delete-resource-policy \
--secret-id <your-secret-name> \
--region us-east-1
Option 2: Replace with a restricted policy
Create a policy that only allows specific principals:
aws secretsmanager put-resource-policy \
--secret-id <your-secret-name> \
--region us-east-1 \
--block-public-policy \
--resource-policy '{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AllowSpecificPrincipal",
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::<ACCOUNT_ID>:role/<ROLE_NAME>"
},
"Action": [
"secretsmanager:GetSecretValue",
"secretsmanager:DescribeSecret"
],
"Resource": "*"
}
]
}'
Replace:
<your-secret-name>with the name or ARN of your secret<ACCOUNT_ID>with the AWS account ID<ROLE_NAME>with the specific IAM role name
Option 3: Add conditions to restrict wildcard principals
If you must use wildcards, add conditions to limit access:
aws secretsmanager put-resource-policy \
--secret-id <your-secret-name> \
--region us-east-1 \
--block-public-policy \
--resource-policy '{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AllowSameAccountOnly",
"Effect": "Allow",
"Principal": "*",
"Action": "secretsmanager:GetSecretValue",
"Resource": "*",
"Condition": {
"StringEquals": {
"aws:PrincipalAccount": "<ACCOUNT_ID>"
}
}
}
]
}'
CloudFormation (optional)
Use this template to create a secret with a properly restricted resource policy:
AWSTemplateFormatVersion: '2010-09-09'
Description: Secrets Manager secret with restricted resource policy
Parameters:
SecretName:
Type: String
Description: Name of the secret
Default: my-application-secret
AllowedPrincipalArn:
Type: String
Description: ARN of the IAM principal allowed to access the secret
Default: arn:aws:iam::123456789012:role/MyApplicationRole
Resources:
MySecret:
Type: AWS::SecretsManager::Secret
Properties:
Name: !Ref SecretName
Description: Application secret with restricted access
GenerateSecretString:
SecretStringTemplate: '{"username": "admin"}'
GenerateStringKey: password
PasswordLength: 32
ExcludeCharacters: '"@/\'
MySecretResourcePolicy:
Type: AWS::SecretsManager::ResourcePolicy
Properties:
SecretId: !Ref MySecret
ResourcePolicy:
Version: '2012-10-17'
Statement:
- Sid: AllowSpecificPrincipal
Effect: Allow
Principal:
AWS: !Ref AllowedPrincipalArn
Action:
- secretsmanager:GetSecretValue
- secretsmanager:DescribeSecret
Resource: '*'
- Sid: DenyPublicAccess
Effect: Deny
Principal: '*'
Action: secretsmanager:*
Resource: '*'
Condition:
Bool:
aws:PrincipalIsAWSService: 'false'
StringNotEquals:
aws:PrincipalAccount: !Ref AWS::AccountId
BlockPublicPolicy: true
Outputs:
SecretArn:
Description: ARN of the created secret
Value: !Ref MySecret
SecretName:
Description: Name of the created secret
Value: !Ref SecretName
Deploy using:
aws cloudformation deploy \
--template-file template.yaml \
--stack-name secrets-manager-restricted \
--parameter-overrides \
SecretName=my-app-secret \
AllowedPrincipalArn=arn:aws:iam::123456789012:role/MyAppRole \
--region us-east-1
Terraform (optional)
terraform {
required_version = ">= 1.0"
required_providers {
aws = {
source = "hashicorp/aws"
version = ">= 4.0"
}
}
}
variable "secret_name" {
description = "Name of the secret"
type = string
default = "my-application-secret"
}
variable "allowed_principal_arn" {
description = "ARN of the IAM principal allowed to access the secret"
type = string
default = "arn:aws:iam::123456789012:role/MyApplicationRole"
}
variable "aws_account_id" {
description = "AWS account ID for condition"
type = string
default = "123456789012"
}
resource "aws_secretsmanager_secret" "this" {
name = var.secret_name
description = "Application secret with restricted access"
}
resource "aws_secretsmanager_secret_policy" "this" {
secret_arn = aws_secretsmanager_secret.this.arn
block_public_policy = true
policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Sid = "AllowSpecificPrincipal"
Effect = "Allow"
Principal = {
AWS = var.allowed_principal_arn
}
Action = [
"secretsmanager:GetSecretValue",
"secretsmanager:DescribeSecret"
]
Resource = "*"
},
{
Sid = "DenyPublicAccess"
Effect = "Deny"
Principal = "*"
Action = "secretsmanager:*"
Resource = "*"
Condition = {
Bool = {
"aws:PrincipalIsAWSService" = "false"
}
StringNotEquals = {
"aws:PrincipalAccount" = var.aws_account_id
}
}
}
]
})
}
output "secret_arn" {
description = "ARN of the created secret"
value = aws_secretsmanager_secret.this.arn
}
output "secret_name" {
description = "Name of the created secret"
value = aws_secretsmanager_secret.this.name
}
Apply using:
terraform init
terraform apply \
-var="secret_name=my-app-secret" \
-var="allowed_principal_arn=arn:aws:iam::123456789012:role/MyAppRole" \
-var="aws_account_id=123456789012"
Verification
After making changes, verify the fix:
-
In the AWS Console, return to your secret and confirm the Resource permissions section shows a restricted policy (or no policy)
-
Verify that Block public access is enabled
-
Re-run the Prowler check:
prowler aws --checks secretsmanager_not_publicly_accessible --region us-east-1
The check should now pass.
Advanced verification with AWS CLI
Check the current policy:
aws secretsmanager get-resource-policy \
--secret-id <your-secret-name> \
--region us-east-1 \
--query 'ResourcePolicy' \
--output text | jq .
Review the output and confirm:
- No
"Principal": "*"without conditions - The
BlockPublicPolicysetting istrue
Validate the policy does not allow public access:
aws secretsmanager validate-resource-policy \
--secret-id <your-secret-name> \
--resource-policy "$(aws secretsmanager get-resource-policy --secret-id <your-secret-name> --query ResourcePolicy --output text)" \
--region us-east-1
This command returns validation results including whether the policy allows public access.
Additional Resources
- AWS Secrets Manager Resource Policies
- Permissions Policy Examples
- Determine Who Has Permissions to Your Secrets
- Block Public Access to Secrets
Notes
-
Block public policy: Enabling this setting prevents anyone from attaching a public policy in the future. This is a safeguard that should generally be enabled.
-
Rotation considerations: If you are using Lambda functions for secret rotation, ensure the Lambda execution role is explicitly allowed in your policy.
-
Cross-account access: If you legitimately need cross-account access, specify the exact account IDs and principal ARNs rather than using wildcards.
-
KMS key policies: If your secret is encrypted with a customer-managed KMS key, also review the KMS key policy to ensure it does not grant overly broad access.
-
AWS service access: Some AWS services need access to secrets. Use service principal conditions (like
aws:PrincipalIsAWSService) carefully to allow only the services you intend.