Back to Blog
medium SEVERITY9 min read

How Hardcoded AWS Secret Access Keys Happen in Configuration Files and How to Fix Them

A hardcoded AWS Secret Access Key pattern was detected in the `settings.example` configuration file of an nginx AWS credentials module. While the value itself was a placeholder string, its format matched a real AWS secret key pattern, making it a dangerous template that could mislead developers into committing real credentials. The fix replaces the lookalike secret value with an unambiguous placeholder that cannot be mistaken for or used as a real credential.

O
By Orbis AppSec
Published August 19, 2026Reviewed August 19, 2026

Answer Summary

A hardcoded AWS Secret Access Key (CWE-798) was found in `settings.example` at line 3, where the value `aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa` matched the format of a real 40-character AWS secret key. This is a Node.js/nginx JavaScript library, so the risk extends to all downstream consumers who copy this example file. The fix replaces the ambiguous placeholder with `<YOUR_AWS_SECRET_ACCESS_KEY>`, a clearly non-functional token that eliminates the risk of accidental credential commits and prevents automated secret-scanning tools from flagging the pattern in derivative projects.

Vulnerability at a Glance

cweCWE-798 (Use of Hard-coded Credentials)
fixReplace the ambiguous placeholder with `<YOUR_AWS_SECRET_ACCESS_KEY>` — an angle-bracket token that is syntactically invalid as a real credential
riskDevelopers copying the example file may accidentally commit real AWS credentials in the same format
languageConfiguration / Environment File
root causeThe placeholder value `aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa` matches the 40-character alphanumeric pattern of a real AWS Secret Access Key
vulnerabilityHardcoded AWS Secret Access Key in Example Configuration

How Hardcoded AWS Secret Access Keys Happen in Configuration Files and How to Fix Them


Vulnerability at a Glance

Field Detail
Vulnerability Hardcoded AWS Secret Access Key in Example Configuration
CWE CWE-798 — Use of Hard-coded Credentials
Language Configuration / Environment File
Risk Developers copying the example file may accidentally commit real credentials in the same format
Root Cause Placeholder value matches the exact format of a real AWS Secret Access Key
Fix Replace with an angle-bracket token that is syntactically invalid as a real credential

Summary

A hardcoded AWS Secret Access Key pattern was detected in the settings.example configuration file of an nginx AWS credentials module. While the value itself was a placeholder string, its format matched a real AWS secret key pattern, making it a dangerous template that could mislead developers into committing real credentials. The fix replaces the lookalike secret value with an unambiguous placeholder that cannot be mistaken for or used as a real credential.


Introduction

The settings.example file in this nginx AWS credentials library exists for one purpose: to show developers exactly what their environment configuration should look like before they deploy. That's a useful thing to have. But there's a subtle trap hiding in example files — if the placeholder values you choose look too much like real secrets, you've created a template that trains developers to put real secrets in the wrong place.

That's precisely what happened here. At line 3 of settings.example, the AWS_SECRET_ACCESS_KEY variable was set to:

AWS_SECRET_ACCESS_KEY=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa

That 40-character all-lowercase string is not a real AWS credential — but it is formatted exactly like one. AWS Secret Access Keys are 40-character base64 strings. Semgrep's rule generic.secrets.security.detected-aws-secret-access-key matched it immediately, and for good reason: any automated tool scanning a repository that inherits this file would flag it as a potential live credential.

This matters especially because this is a Node.js library — vulnerabilities and patterns in it propagate to every downstream project that uses it as a dependency or copies its configuration as a starting point.


The Vulnerability Explained

What Made This Risky?

AWS Secret Access Keys follow a well-known format: exactly 40 characters, alphanumeric, base64-encoded. Secret scanning tools — including Semgrep, TruffleHog, GitLeaks, and GitHub's own push protection — use regular expressions to match this pattern. The original placeholder:

# settings.example — BEFORE the fix (line 3)
AWS_SECRET_ACCESS_KEY=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa

passes that regex check. The string is 40 characters long and composed entirely of valid base64 characters. From a scanner's perspective, this is indistinguishable from a real secret.

The Exploit Primitive

The term "exploit primitive" is apt here. The value itself isn't exploitable — aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa won't authenticate against any real AWS account. But this pattern creates two concrete risks:

Risk 1: Developer Confusion
A developer sets up the project for the first time, copies settings.example to .env, and replaces the obviously fake values — but misses AWS_SECRET_ACCESS_KEY because it already looks like it has a value. They paste their real key next to it in a comment, or accidentally leave a real key in a file they thought was already sanitized.

Risk 2: Downstream Scanner Fatigue
Projects that inherit or copy this configuration will have Semgrep, TruffleHog, or their CI pipeline flag this line on every scan. Teams that see this false positive repeatedly may begin to suppress or ignore the rule — exactly the behavior that allows real secrets to slip through undetected.

Real-World Context: awscredentials.js

This settings.example file is the configuration template for common/etc/nginx/include/awscredentials.js — a module that handles AWS credential fetching via IMDS, ECS, EKS Pod Identity Agent, and STS. The credentials configured here are used to authenticate outbound calls to AWS services. A real secret key in this position would grant full programmatic AWS access scoped to the associated IAM role or user.

The full context of the vulnerable file:

# settings.example — BEFORE the fix
S3_BUCKET_NAME=my-bucket
AWS_ACCESS_KEY_ID=ZZZZZZZZZZZZZZZZZZZZ
AWS_SECRET_ACCESS_KEY=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa   # ← flagged
AWS_SESSION_TOKEN=bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
S3_SERVER=s3.us-east-1.amazonaws.com
S3_SERVER_PORT=443

Notice that AWS_ACCESS_KEY_ID uses ZZZZZZZZZZZZZZZZZZZZ — clearly fake, all uppercase Z's — and AWS_SESSION_TOKEN uses bbb... — also clearly fake. Only AWS_SECRET_ACCESS_KEY used a value that could pass a format check as real.


The Fix

The fix is a single-line change in settings.example, but it carries meaningful security weight:

- AWS_SECRET_ACCESS_KEY=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
+ AWS_SECRET_ACCESS_KEY=<YOUR_AWS_SECRET_ACCESS_KEY>

Why This Specific Change Works

The replacement value <YOUR_AWS_SECRET_ACCESS_KEY> has three properties that make it unambiguously safe:

  1. Syntactically invalid as a credential: The angle brackets < and > are not valid base64 characters. No AWS SDK, CLI, or API will accept this as a real secret key.

  2. Immediately recognizable as a placeholder: The <YOUR_...> convention is a well-established documentation pattern. Any developer reading this file knows they need to replace it.

  3. Won't trigger secret scanners: Regex patterns for AWS Secret Access Keys require 40 alphanumeric characters. The angle brackets break the match, so this value will not generate false positives in Semgrep, TruffleHog, or GitHub's push protection.

Before and After

# BEFORE — settings.example line 3
AWS_SECRET_ACCESS_KEY=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa

# AFTER — settings.example line 3
AWS_SECRET_ACCESS_KEY=<YOUR_AWS_SECRET_ACCESS_KEY>

The change is minimal in size but significant in intent: it removes an exploit primitive that could contribute to credential exposure in downstream projects.


Prevention & Best Practices

1. Use Unambiguous Placeholders in Example Files

Always use placeholder formats that cannot be confused with real values. Preferred patterns:

# Good — angle brackets signal "replace this"
AWS_SECRET_ACCESS_KEY=<YOUR_AWS_SECRET_ACCESS_KEY>

# Good — descriptive and clearly fake
DATABASE_PASSWORD=REPLACE_WITH_YOUR_DATABASE_PASSWORD

# Bad — looks like it might be real
AWS_SECRET_ACCESS_KEY=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa

# Bad — too short to be real but still triggers some scanners
API_KEY=abc123

2. Add Secret Scanning to Your CI Pipeline

Integrate secret scanning at the point of code commit, not just in periodic audits:

  • Semgrep: Use the generic.secrets ruleset to catch AWS keys, GitHub tokens, and other credential formats
  • TruffleHog: Scans git history for high-entropy strings and known secret patterns
  • GitHub Push Protection: Blocks pushes containing detected secrets before they reach the remote
# Example: Semgrep in GitHub Actions
- name: Semgrep secret scan
  uses: semgrep/semgrep-action@v1
  with:
    config: p/secrets

3. Use .env.example with a .gitignore for .env

The standard pattern for environment configuration:

# .gitignore
.env
*.env
!.env.example
!settings.example

This ensures the example file (with placeholders) is committed, while the real file (with actual credentials) is never tracked.

4. Prefer IAM Roles Over Static Credentials

For production deployments, avoid static AWS_ACCESS_KEY_ID / AWS_SECRET_ACCESS_KEY pairs entirely. The awscredentials.js module already supports IMDS and ECS credential providers — use them:

  • EC2 instances: Use instance profiles (IMDS)
  • ECS tasks: Use task roles (ECS metadata endpoint)
  • EKS pods: Use Pod Identity or IRSA (EKS Pod Identity Agent)

Static credentials should be a last resort, not a default.

5. Rotate Credentials Immediately If Exposure Is Suspected

If a real AWS Secret Access Key is ever committed to a repository — even briefly — treat it as compromised:

  1. Deactivate the key immediately in the AWS IAM console
  2. Generate a new key pair
  3. Audit CloudTrail logs for unauthorized use during the exposure window
  4. Review IAM policies attached to the key for blast radius assessment

Security Standards

  • CWE-798: Use of Hard-coded Credentials — https://cwe.mitre.org/data/definitions/798.html
  • OWASP A07:2021 — Identification and Authentication Failures covers credential management weaknesses
  • OWASP Secrets Management Cheat Sheet provides detailed guidance on handling secrets safely

Key Takeaways

  • The format of a placeholder matters as much as its value: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa is 40 alphanumeric characters — the exact shape of a real AWS Secret Access Key — and will trigger secret scanners even though it's fake.
  • Example configuration files are a common vector for accidental credential exposure: Developers copy them verbatim; if the placeholder looks real, a real value might end up next to it or replacing it without scrutiny.
  • Angle-bracket placeholders (<YOUR_VALUE>) are the safest choice: They are syntactically invalid for every credential format, visually obvious, and universally recognized as "replace this."
  • This library's settings.example configures credentials for awscredentials.js, which makes outbound calls to AWS IMDS, ECS, EKS, and STS — meaning a real leaked key here could expose significant AWS infrastructure.
  • False positives from fake-but-realistic placeholders cause scanner fatigue, which is itself a security risk: teams that suppress noisy rules stop catching real secrets.

How Orbis AppSec Detected This

  • Source: The AWS_SECRET_ACCESS_KEY variable assignment at settings.example:3, where the placeholder value matched the 40-character alphanumeric pattern of a real AWS Secret Access Key
  • Sink: The value as written could be directly copied into a real .env file and used to authenticate AWS API calls via the awscredentials.js credential fetching module
  • Missing control: No unambiguous signal in the placeholder value that it must be replaced; the format was indistinguishable from a real credential to both automated scanners and human reviewers
  • CWE: CWE-798 — Use of Hard-coded Credentials
  • Fix: Replaced aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa with <YOUR_AWS_SECRET_ACCESS_KEY>, an angle-bracket token that is syntactically invalid as a credential and clearly communicates intent to the reader

Orbis AppSec automatically detected this vulnerability and opened a pull request with the fix. Try Orbis AppSec on your repositories to find and fix issues like this automatically.


Conclusion

A single line in an example configuration file — AWS_SECRET_ACCESS_KEY=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa — illustrates how easy it is to inadvertently introduce a security risk through well-intentioned documentation. The value was never meant to be a real credential, but its format made it indistinguishable from one to both automated tools and developers under time pressure.

The fix is as simple as it is effective: replace any placeholder that could pass a format check for a real credential with one that cannot. <YOUR_AWS_SECRET_ACCESS_KEY> communicates the same information to a developer setting up the project, but eliminates the risk of scanner fatigue, accidental credential commits, and downstream confusion in projects that inherit this configuration.

For teams building libraries and frameworks — especially those that handle authentication and cloud credentials like awscredentials.js — the example files you ship are part of your security surface. Treat them accordingly.


References

Frequently Asked Questions

What is a hardcoded AWS Secret Access Key vulnerability?

It occurs when a real or realistic-looking AWS Secret Access Key is embedded directly in source code or configuration files, making it discoverable by anyone with repository access and potentially usable to authenticate against AWS services.

How do you prevent hardcoded secrets in configuration files?

Use clearly non-functional placeholders (e.g., `<YOUR_SECRET_HERE>`), enforce secret scanning in CI/CD pipelines, and rely on environment variable injection or secrets managers at runtime rather than storing credentials in files.

What CWE is hardcoded credentials?

CWE-798 — "Use of Hard-coded Credentials." It describes situations where software contains credentials that cannot be easily changed and are accessible to attackers who can read the source.

Is using a placeholder value enough to prevent hardcoded credential vulnerabilities?

Only if the placeholder is clearly non-functional. A string like `aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa` passes format checks for a real AWS secret key, so secret scanners and developers may treat it as real. An angle-bracket token like `<YOUR_AWS_SECRET_ACCESS_KEY>` cannot be a valid credential.

Can static analysis detect hardcoded AWS credentials?

Yes. Tools like Semgrep, TruffleHog, and GitLeaks use regex patterns that match the 40-character alphanumeric format of AWS Secret Access Keys and will flag both real secrets and realistic-looking placeholders.

View the Security Fix

Check out the pull request that fixed this vulnerability

View PR #549

Related Articles

high

How hardcoded AWS Access Key ID exposure happens in YAML template files and how to fix it

A hardcoded AWS Access Key ID (`AKIAVCODYLSA53PQK4ZA`) was discovered embedded in a signed S3 URL within the `CVE-2024-51482.yaml` nuclei template file. This credential, while part of a pre-signed URL reference link, was flagged as a high-severity finding because it exposes a real AWS access key in a public repository. The fix removes the entire reference URL containing the embedded credential.

critical

Critical Command Injection Fix: How os.system() Put AWS Workflows at Risk

A critical command injection vulnerability (CWE-78) was discovered and patched in `utils/aws/resume.py`, where unsanitized user input was passed directly to `os.system()`, allowing attackers to execute arbitrary shell commands. The fix replaces the dangerous `os.system()` call with Python's `subprocess` module, which provides proper argument separation and eliminates shell interpretation of metacharacters. This post breaks down how the vulnerability worked, how it was exploited, and what every d

medium

How XML External Entity (XXE) Injection Happens in Python and How to Fix It

A critical XML External Entity (XXE) vulnerability was discovered in `scripts/screenshots/ui.py` where the native Python `xml.etree.ElementTree` library was used without XXE protections. The fix replaces the vulnerable import with `defusedxml.ElementTree`, which disables external entity processing by default and prevents attackers from exploiting XML parsing to access sensitive files or execute denial-of-service attacks.

medium

How Hardcoded AWS Credentials Happen in Node.js Configuration Files and How to Fix It

A critical security issue was discovered in the S3 Express deployment configuration file where an AWS Secret Access Key was hardcoded as a placeholder example. This vulnerability could allow attackers to gain unauthorized access to AWS resources if the example file was accidentally deployed to production or committed to version control without proper sanitization.

medium

How Denial of Service via ZIP Bomb happens in Node.js adm-zip and how to fix it

The cc-viewer application was vulnerable to Denial of Service attacks through the adm-zip library (version 0.5.17), which could be exploited using specially crafted ZIP files that trigger excessive memory allocation. Upgrading to adm-zip 0.6.0 resolves CVE-2026-39244 by implementing proper safeguards against ZIP bomb attacks and malicious archive structures.

medium

How GitHub Actions Mutable Action Tags Enable Supply-Chain Attacks and How to Fix Them

A GitHub Actions workflow was using `actions/checkout@v1`, a mutable tag reference that could be silently repointed by the action owner to inject malicious code. This supply-chain vulnerability was fixed by pinning the action to a specific commit SHA (`11bd71901bbe5b1630ceea73d27597364c9af683`), ensuring the workflow always executes verified, immutable code.