Back to Checks
Step Functions state machine has no sensitive credentials in its definition
stepfunctions_statemachine_no_secrets_in_definition
Service: stepfunctionsSeverity: critical
OfficialBuilt-inVerified
Categories:secrets
Resource Type:AwsStepFunctionStateMachine
AWS Step Functions state machines are inspected for hardcoded secrets (keys, tokens, passwords) embedded directly in the state machine definition (Amazon States Language JSON).
Such values indicate sensitive data is stored directly in task parameters instead of being sourced securely.
Risk
Plaintext secrets in state machine definitions reduce confidentiality: values can be viewed in the AWS Console, CLI, and may leak into execution logs or public outputs. Compromised credentials enable unauthorized AWS actions, lateral movement, and data exfiltration.
Run this check with Prowler
Run in Prowler CloudRun
prowler aws --checks stepfunctions_statemachine_no_secrets_in_definitionRecommendation
Store secrets outside the state machine definition and retrieve them securely at runtime using AWS Secrets Manager or AWS Systems Manager Parameter Store.
- Use the
aws-sdk:secretsmanager:getSecretValueintegration to fetch secrets dynamically - Enforce least privilege on the state machine IAM role
- Rotate secrets regularly and never embed them in the definition
Remediation
Terraform
resource "aws_sfn_state_machine" "<example_resource_name>" {
name = "<example_resource_name>"
role_arn = "<example_resource_arn>"
definition = jsonencode({
Comment = "Example state machine"
StartAt = "MyTask"
States = {
MyTask = {
Type = "Task"
Resource = "arn:aws:states:::aws-sdk:secretsmanager:getSecretValue"
Parameters = {
SecretId = "<example_secret_name>" # Reference secret by name, never hardcode value
}
End = true
}
}
})
}NativeIaC
Resources:
<example_resource_name>:
Type: AWS::StepFunctions::StateMachine
Properties:
StateMachineName: <example_resource_name>
RoleArn: <example_resource_arn>
DefinitionString: |
{
"Comment": "Example state machine",
"StartAt": "MyTask",
"States": {
"MyTask": {
"Type": "Task",
"Resource": "arn:aws:states:::aws-sdk:secretsmanager:getSecretValue",
"Parameters": {
"SecretId": "<example_secret_name>"
},
"End": true
}
}
}Other
1In AWS Console, go to Step Functions and open your state machine
2Click Edit
3Remove any hardcoded secrets from the definition
4Use AWS Secrets Manager or Parameter Store to retrieve secrets at runtime
5Grant the state machine IAM role permission to access the secret
6Save the updated definition
Check Code
from prowler.lib.check.models import Check, Check_Report_AWS
from prowler.lib.utils.utils import (
SecretsScanError,
annotate_verified_secrets,
detect_secrets_scan_batch,
)
from prowler.providers.aws.services.stepfunctions.stepfunctions_client import (
stepfunctions_client,
)
class stepfunctions_statemachine_no_secrets_in_definition(Check):
"""Check that AWS Step Functions state machine definitions contain no hardcoded secrets."""
def execute(self) -> list[Check_Report_AWS]:
findings = []
secrets_ignore_patterns = stepfunctions_client.audit_config.get(
"secrets_ignore_patterns", []
)
validate = stepfunctions_client.audit_config.get("secrets_validate", False)
state_machines = list(stepfunctions_client.state_machines.values())
# Collect one payload per state machine (its definition) and scan them
# all in batched Kingfisher invocations instead of one subprocess each.
def payloads():
for index, state_machine in enumerate(state_machines):
if state_machine.definition:
yield index, state_machine.definition
scan_error = None
try:
batch_results = detect_secrets_scan_batch(
payloads(), excluded_secrets=secrets_ignore_patterns, validate=validate
)
except SecretsScanError as error:
batch_results = {}
scan_error = error
for index, state_machine in enumerate(state_machines):
report = Check_Report_AWS(metadata=self.metadata(), resource=state_machine)
report.status = "PASS"
report.status_extended = f"No secrets found in Step Functions state machine {state_machine.name} definition."
if state_machine.definition:
if scan_error:
report.status = "MANUAL"
report.status_extended = (
f"Could not scan Step Functions state machine "
f"{state_machine.name} definition for secrets: {scan_error}; "
"manual review is required."
)
findings.append(report)
continue
detect_secrets_output = batch_results.get(index)
if detect_secrets_output:
secrets_string = ", ".join(
[
f"{secret['type']} on line {secret['line_number']}"
for secret in detect_secrets_output
]
)
report.status = "FAIL"
report.status_extended = (
f"Potential {'secrets' if len(detect_secrets_output) > 1 else 'secret'} "
f"found in Step Functions state machine {state_machine.name} definition "
f"-> {secrets_string}."
)
annotate_verified_secrets(report, detect_secrets_output)
findings.append(report)
return findingsReferences
- https://docs.aws.amazon.com/step-functions/latest/dg/concepts-amazon-states-language.html
- https://docs.aws.amazon.com/step-functions/latest/dg/security-best-practices.html
- https://docs.aws.amazon.com/secretsmanager/latest/userguide/integrating_how-services-use-secrets_step-functions.html
- https://docs.aws.amazon.com/systems-manager/latest/userguide/integration-ps-secretsmanager.html