Skip to content

VNX-SEC-003: AWS secret access key

Reports a hard-coded, high-entropy 40-character AWS secret access key assigned to an AWS secret-key name, a Terraform AWS secret_key, Go's NewStaticCredentials, or an SDK credentials constructor (Java, .NET, Ruby, PHP), excluding the documented example key, placeholders, hex digests, environment references and test files.

Updated 02 Nov 2026Checked against cli@de9b52a
Field Value
Rule ID VNX-SEC-003
Severity Critical
SARIF level error
Kind secrets
Languages Any file
CWE CWE-798
CAPEC CAPEC-191
ATT&CK T1552.001
CWSS vector CWSS:1.0/TI:C/AP:A/AL:A/IC:N/FC:LT/RP:N/RL:A/AV:I/AS:N/IN:A/SC:A/BI:C/DI:H/EX:H/EC:N/P:W
Tags secrets aws credentials
Source internal/sast/rules/vnx-sec-003.rego

Overview

This rule detects AWS secret access keys embedded in source files by matching variable names such as aws_secret, secret_access_key, or aws_secret_access_key assigned to a 40-character base64 string. The AWS secret access key is the second half of the AWS credential pair; alone it is insufficient for authentication, but when combined with an access key ID (detected by VNX-SEC-001) it provides complete programmatic access to AWS APIs. These two values almost always appear together in the same file or nearby.

Why This Matters

AWS credentials are one of the most targeted secrets in automated scanning operations. Malicious bots continuously crawl public repositories for patterns matching AWS secret keys; a key can be abused for crypto mining, data exfiltration, or infrastructure destruction within minutes of exposure. Secret access keys are 40 characters of base64 and do not have a distinctive prefix like access key IDs, making them harder to detect visually, but the rule matches them by context (the surrounding variable name).

Unlike a password that might be rotated periodically, AWS keys remain valid until explicitly rotated or deleted. Every git clone, CI/CD artifact, or log containing the key becomes a potential leak vector. VNX-SEC-001 and VNX-SEC-003 are designed to be used together since these two values always travel as a pair.

What Gets Flagged

A 40-character base64 value with high entropy, where AWS says it is a secret access key:

  • assigned to aws_secret_access_key, AWS_SECRET_ACCESS_KEY, secret_access_key, secretAccessKey, SecretAccessKey, aws_secret_key or aws_secret in code, JSON/YAML, .env, shell export or the ~/.aws/credentials format;
  • the secret_key argument of a Terraform/HCL file that configures AWS;
  • the second argument of Go’s credentials.NewStaticCredentials / NewStaticCredentialsProvider;
  • a quoted secret passed to an SDK credentials constructor (new BasicAWSCredentials(id, "...") in Java and .NET, AwsBasicCredentials.create, Ruby Aws::Credentials.new, PHP Credentials), including when the argument is on one of the next three lines;
  • the 'secret' entry of a PHP SDK 'credentials' => [...] array.
# FLAGGED: shared credentials file
[default]
aws_secret_access_key = <40-character secret>
// FLAGGED: static credentials in Go
creds := credentials.NewStaticCredentials(keyID, "<40-character secret>", "")

Not flagged:

  • AWS’s documented example key (ending EXAMPLEKEY) and placeholders.
  • 40-character hex digests (a SHA-1 is not a secret key).
  • Environment and CI-secret references (os.environ[...], ${{ secrets.AWS_SECRET_ACCESS_KEY }}).
  • A generic SECRET_KEY (Django’s, for example) outside an AWS Terraform file.
  • Test, fixture, vendored and generated files.

The paired access key ID is VNX-SEC-001.

Remediation

  1. Rotate the access key immediately. In the AWS Console go to IAM → Users → your user → Security credentials → Create access key. Delete the old key once the new one is confirmed working.

  2. Verify whether the key was used maliciously. Check AWS CloudTrail for API calls from the compromised key. Look especially for iam:CreateUser, ec2:RunInstances, s3:GetObject, and sts:AssumeRole calls that you did not make.

  3. Remove from source code. Replace hardcoded values with environment variable lookups:

# SAFE: load from environment
import boto3
import os

session = boto3.Session(
    aws_access_key_id=os.environ['AWS_ACCESS_KEY_ID'],
    aws_secret_access_key=os.environ['AWS_SECRET_ACCESS_KEY']
)

For services running on AWS (EC2, ECS, Lambda), use IAM instance roles or task roles, no static keys are needed at all. The SDK picks up credentials automatically from instance metadata.

  1. Use AWS Secrets Manager for non-AWS-hosted workloads:
# SAFE: fetch from AWS Secrets Manager at runtime
import boto3
import json

def get_aws_credentials():
    client = boto3.client('secretsmanager', region_name='us-east-1')
    secret = client.get_secret_value(SecretId='prod/app/aws-credentials')
    return json.loads(secret['SecretString'])
  1. Scan git history for any traces of the key, then rewrite history with git-filter-repo if found:
git filter-repo --replace-text <(echo 'wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY==>REDACTED')
gitleaks detect --source . --verbose
  1. Set environment variables in CI/CD. Use GitHub Actions secrets, GitLab CI/CD variables, or your pipeline’s secrets management, never hardcode in workflow files either.

References

Suppressing this rule

Put a nosec comment on the line, or on line 1 to cover the whole file. Use the comment marker of the file’s language (#, //, -- or ;):

# nosec vnx-sec-003
// nosec vnx-sec-003

Or record an ignore rule: vulnetix ignore add --rule vnx-sec-003 --reason "...". See Suppressing findings.

Edit this pageSources: cli/internal/sast/rules/vnx-sec-003.rego, cli/internal/sast/docs/vnx-sec-003.md
esc
↑ ↓ move↵ openSearches in your browser. A key like autonomy.max or a code like threshold_required jumps straight to it.