Skip to main content

Free 30-min security demo Book Now

Offensive360 Offensive360
Vulnerability Research

CWE-798 Complete Reference: What It Is, How It's Exploited & Fix

CWE-798 (Hard-Coded Credentials): definition, CVSS scores, real exploit paths, how SAST tools detect it, and the complete 6-step remediation sequence with code examples.

Offensive360 Security Research Team — min read
CWE-798 cwe 798 use of hard-coded credentials hardcoded credentials hardcoded passwords SAST secrets management CWE-798 remediation hard-coded credentials cwe798 hardcoded secrets application security

CWE-798 — “Use of Hard-Coded Credentials” — is one of the most exploited vulnerability classes in enterprise software. It appears in the CWE Top 25 Most Dangerous Software Weaknesses every year and is flagged as Critical or High by every major SAST scanner. Despite being well-understood for decades, it is consistently found in production codebases across all languages and industries.

This reference covers everything you need to know about CWE-798: the official definition, CVSS scoring, real exploit paths, how SAST tools detect it, and the complete step-by-step remediation.


Official Definition

CWE ID: CWE-798
Name: Use of Hard-Coded Credentials
Abstraction: Base
Structure: Simple
Parent: CWE-344 (Use of Invariant Value in Dynamically Changing Context)

The MITRE CWE definition states:

“The software contains hard-coded credentials, such as a password or cryptographic key, which it uses for its own inbound authentication, outbound communication to external components, or encryption of internal data.”

Subtypes:

  • CWE-259 — Use of Hard-Coded Password (specific to passwords)
  • CWE-321 — Use of Hard-Coded Cryptographic Key (specific to cryptographic key material)

When a SAST tool reports CWE-798, it may cite CWE-259 or CWE-321 depending on whether the hardcoded value is a password or a cryptographic key. The remediation is the same.


What Counts as a CWE-798 Finding

CWE-798 covers any authentication secret or cryptographic key embedded as a literal value in source code, configuration files committed to version control, or compiled into a binary. This includes:

Passwords and passphrases:

private static final String DB_PASSWORD = "Pr0dS3cret!";  // CWE-798

API keys and bearer tokens:

STRIPE_SECRET_KEY = "sk_live_51HabcXYZ123..."  # CWE-798
SENDGRID_API_KEY = "SG.abcdefghijklmnopqrst"   # CWE-798

Connection strings containing credentials:

const DB_URL = "postgresql://admin:MyP@[email protected]:5432/app";  // CWE-798

Private cryptographic keys:

private const string PrivateKey = "-----BEGIN RSA PRIVATE KEY-----\nMIIE...";  // CWE-798 (CWE-321)

Base64-encoded credentials — encoding is not encryption:

$credentials = base64_encode("admin:S3cur3P@ss");  // Still CWE-798

HMAC signing secrets:

var jwtSecret = []byte("mysupersecretkey123")  // CWE-798 if hardcoded

Default credentials in firmware or containerized applications:

DEFAULT_ADMIN_PASSWORD = "admin"  # Ships with every deployment — CWE-798

Why CWE-798 Is Rated Critical

SAST tools rate CWE-798 as High or Critical because the exploit path is trivially short. There is no vulnerability chain required — the credential is already exposed.

CVSS Scoring for CWE-798

CWE-798 does not have a single CVSS score — the score varies based on what the hardcoded credential protects. Typical ranges:

Credential TypeCVSS Base ScoreRationale
Database admin password9.1 – 9.8 (Critical)Full data access or RCE via xp_cmdshell
AWS/GCP/Azure root key9.0 – 10.0 (Critical)Full cloud account compromise
JWT signing secret8.1 – 9.8 (Critical)Authentication bypass, identity forgery
External API key (read-only)5.3 – 7.5 (Medium–High)Data exposure but no write access
Local development credential2.0 – 4.0 (Low)Risk limited to non-production environments

In practice, most hardcoded production credentials are scored High (7.0–8.9) to Critical (9.0+) because they protect authentication or data access paths in production systems.


The Three Exploit Paths for CWE-798

Path 1: Version Control Repository Exposure

This is the most common real-world exploit path. A developer commits source code containing a hardcoded credential to a Git repository. The repository is accessed by current or former employees, contractors, or is accidentally made public.

# Anyone with repository access can recover the credential in seconds
git log --all -p | grep -A3 -B3 "password\|api_key\|secret"

# Or using dedicated tools
trufflehog git file://./repo
gitleaks detect --source . --verbose

Critical fact: Removing the credential in a subsequent commit does not remove it from the repository’s commit history. The credential is permanently recoverable via git log until the history is explicitly rewritten with git-filter-repo.

Automated scanners (operated by both security researchers and threat actors) continuously scrape GitHub, GitLab, and Bitbucket for exposed credential patterns. A publicly accessible repository containing hardcoded credentials is typically compromised within minutes of the initial push.

Path 2: Binary/APK Reverse Engineering

For compiled applications — Java JARs, .NET assemblies, Android APKs, iOS IPAs, and native binaries — hardcoded strings are recoverable via decompilation or string extraction:

# Android APK — extract and search for credentials
jadx -d output/ target.apk
grep -r "api_key\|password\|secret\|token" output/ --include="*.java"

# .NET assembly — decompile with dnSpy or ILSpy
# Java JAR — decompile with cfr or procyon
java -jar cfr.jar target.jar --outputdir decompiled/
grep -r "password\|secret" decompiled/

# Native binary — extract printable strings
strings ./binary | grep -E "[a-zA-Z0-9+/]{20,}={0,2}"  # base64-ish strings
strings ./binary | grep -Ei "pass|key|token|secret|api"

Mobile applications distributed through app stores are particularly vulnerable. Any API key, signing secret, or backend credential embedded in an APK or IPA is effectively public once the application is distributed.

Path 3: Docker Image and Container Inspection

Credentials set via ENV instructions in Dockerfiles or embedded during image build are stored in image layers and are recoverable by anyone who can pull the image:

# Inspect image layers for credentials
docker history --no-trunc myapp:latest
docker inspect myapp:latest | jq '.[].Config.Env'

# If the image is in a registry, pull and inspect it
docker pull myregistry.example.com/myapp:latest
docker run --rm myapp:latest env | grep -iE "pass|key|secret|token"

If the container image is pushed to a container registry — even a private one — any principal with pull access can extract the credentials.


How SAST Tools Detect CWE-798

Different SAST tools use different detection approaches. Here is how the major tools find CWE-798:

Detection Method 1: Credential Pattern Matching

The simplest approach — match variable names and string literal patterns against a list of credential-indicator terms:

  • Variable names containing: password, passwd, pwd, secret, api_key, apikey, token, key, credentials, auth
  • String patterns matching common credential formats (AWS access key patterns, JWT header patterns, private key headers)

This approach catches most obvious cases but produces false positives on non-sensitive variables with similar names (e.g., password_length = 12 or key_exists = true).

Detection Method 2: Taint Flow to Authentication Sinks

More accurate SAST tools (including Checkmarx and Offensive360) combine pattern matching with data flow analysis — tracking whether a string literal flows into a security-sensitive method call:

// SAST tracks this flow:
String dbPass = "S3cur3P@ss";           // Source: string literal with "pass" in name
                                         // ↓
DriverManager.getConnection(url, user, dbPass);  // Sink: authentication method
// → CWE-798 finding: string literal flows to authentication sink

The taint analysis identifies:

  1. Source: A string literal with a credential-indicator variable name
  2. Sink: An authentication, encryption, or connection method
  3. Path: Direct literal-to-sink flow without loading from external configuration

Checkmarx CWE-798 Detection

Checkmarx names this finding:

  • Use_Of_Hard_Coded_Password (CxSAST)
  • Hardcoded_Credentials (Checkmarx One)

A typical Checkmarx CWE-798 result:

Severity: High
CWE: CWE-798
Query: Use_Of_Hard_Coded_Password
File: src/config/DatabaseConfig.java
Line: 15
Source: "S3cur3P@ssw0rd!"
Sink: DriverManager.getConnection(url, user, password)

Common Checkmarx CWE-798 false positives:

  • Unit test credentials: "test_password123" in test files
  • Placeholder strings: "YOUR_API_KEY_HERE", "REPLACE_ME"
  • Non-credential variables named like credentials: int maxPasswordLength = 32;

To suppress a false positive in Checkmarx, use an inline comment annotation rather than marking it as a false positive in the UI without justification.

Fortify SCA CWE-798 Detection

Fortify names this finding:

  • Password Management: Hard-Coded Password

Fortify additionally detects:

  • JDBC URL strings containing credentials: jdbc:postgresql://user:pass@host/db
  • Spring @Value("hardcoded_value") annotations
  • Byte array literals flowing to new SecretKeySpec(new byte[]{...})

Offensive360 CWE-798 Detection

Offensive360 SAST detects CWE-798 across 60+ languages using combined pattern matching and data-flow analysis. The detection includes:

  • String literals with credential-pattern variable names
  • Embedded connection strings with user:pass@host format
  • Base64-decoded values flowing to authentication methods
  • Cryptographic key material as byte array literals
  • ENV instructions in Dockerfiles and docker-compose credential fields
  • Kubernetes secrets YAML with plaintext values

Each finding includes file path, line number, detected credential type (password / API key / connection string / private key), and a direct remediation link.


Complete Remediation Sequence

Step 1: Rotate the Credential — Before Anything Else

Treat the credential as already compromised. Rotate it before making any code changes:

  • Change the database password
  • Revoke and reissue the API key
  • Regenerate the JWT signing secret
  • Update all dependent systems with the new credential

If you wait until after the code fix to rotate, every day of delay is a day the exposed credential can be exploited. Start with rotation.

Step 2: Check the Scope of Exposure in Git History

# Determine how long the credential has been in history
git log --all -p -S "the_actual_credential_value" | head -100

# Check if it's in any branch, not just main
git log --all --oneline -- path/to/affected/file

# Check if the repository has ever been public
# (Review GitHub/GitLab audit logs for public visibility events)

Step 3: Replace With Environment Variable or Secrets Manager

Minimum viable fix (resolves the SAST finding):

Move the credential to an environment variable.

# Python — before
DATABASE_PASSWORD = "S3cur3P@ss"

# Python — after
import os
DATABASE_PASSWORD = os.environ["DATABASE_PASSWORD"]
// Java Spring — before
private static final String DB_PASS = "S3cur3P@ss";

// Java Spring — after
@Value("${DB_PASSWORD}")
private String dbPass;
// C# — before
private const string ConnStr = "Host=prod-db;Password=S3cur3P@ss";

// C# — after
private readonly string _connStr;
public MyRepo(IConfiguration config) =>
    _connStr = config.GetConnectionString("DefaultConnection");
// Node.js — before
const dbPassword = "S3cur3P@ss";

// Node.js — after
require('dotenv').config();
const dbPassword = process.env.DB_PASSWORD;

Production-grade fix (secrets manager):

EnvironmentRecommended Solution
AWSAWS Secrets Manager — automatic rotation, fine-grained IAM
AzureAzure Key Vault — managed rotation, Azure AD integration
GCPGCP Secret Manager — IAM-controlled, audit logging
Multi-cloud / on-premHashiCorp Vault — dynamic secrets, policy-based access
Small teamsDoppler or Infisical — developer-friendly SaaS
Any (minimum viable)Environment variables via CI/CD secrets store
# Python with AWS Secrets Manager
import boto3, json

def get_secret(secret_name: str) -> dict:
    client = boto3.client("secretsmanager", region_name="us-east-1")
    return json.loads(
        client.get_secret_value(SecretId=secret_name)["SecretString"]
    )

creds = get_secret("prod/myapp/database")
db_password = creds["password"]

Step 4: Clean the Credential from Git History

# Install git-filter-repo (preferred over git-filter-branch)
pip install git-filter-repo

# Replace all occurrences of the credential in all commits
git filter-repo --replace-text <(echo 'S3cur3P@ss==>[REDACTED]')

# Force-push all branches
git push origin --force --all
git push origin --force --tags

After cleaning:

  • All collaborators must delete local clones and re-clone fresh
  • Invalidate any CI/CD caches or artifact stores that may have cloned the repository before the cleanup
  • If the repository was ever public, treat the credential as permanently compromised regardless of history cleanup

Step 5: Add Prevention Controls

Pre-commit hook with gitleaks:

# Install gitleaks
brew install gitleaks          # macOS
scoop install gitleaks         # Windows
apt-get install gitleaks       # Debian/Ubuntu

# Add as pre-commit hook
cat > .git/hooks/pre-commit << 'EOF'
#!/bin/sh
gitleaks protect --staged --redact --verbose
if [ $? -ne 0 ]; then
  echo "Potential credential detected. Commit blocked."
  exit 1
fi
EOF
chmod +x .git/hooks/pre-commit

Or use pre-commit (the framework):

# .pre-commit-config.yaml
repos:
  - repo: https://github.com/gitleaks/gitleaks
    rev: v8.18.4
    hooks:
      - id: gitleaks

SAST in CI/CD pipeline:

The most effective prevention is running a SAST scan with CWE-798 detection on every pull request. Offensive360’s CI/CD integrations support GitHub Actions, GitLab CI, Azure DevOps, Jenkins, and TeamCity — blocking merges when hardcoded credentials are detected before the code ever reaches a repository branch.

Step 6: Re-scan to Confirm the Finding Is Resolved

After making the code change and credential rotation, run the SAST scan again. CWE-798 findings resolve when the literal string value is removed from source code. If the finding persists, verify that:

  • The credential was removed from all files (not just the one in the finding)
  • The credential is not embedded in a configuration file that is committed to version control (e.g., appsettings.json, .env accidentally committed, docker-compose.yml)
  • The credential is not present in any logging or debug output that gets captured in version control

CWE-798 in Compliance Frameworks

CWE-798 maps to explicit requirements in every major compliance framework:

FrameworkRequirement
PCI-DSS 4.0Req. 8.6.1 — System/application accounts must not use shared or hard-coded credentials
OWASP ASVS 4.0V2.10.1 — No hard-coded passwords in the codebase
SOC 2CC6.1 — Logical access controls (hardcoded credentials violate the principle of least privilege)
ISO 27001A.9.4.3 — Password management systems must be interactive; hardcoded credentials are explicitly prohibited
NIST SP 800-53IA-5 (Authenticator Management) — credentials must be changeable without redeployment
HIPAA§164.312(d) — Authentication controls; hardcoded credentials undermine authentication assurance

A CWE-798 finding discovered during a pre-audit SAST scan is a compliance blocker in all of the above frameworks and will be flagged as a material finding by auditors.


Frequently Asked Questions

Does CWE-798 apply to development/test credentials?

Yes — SAST tools flag CWE-798 in test files as well as production code. Test-only credentials (e.g., "test_password_local_only") that genuinely cannot be used against production systems may be marked as false positives in your SAST platform. Document the suppression with a specific justification. Avoid the habit of hardcoding “test” credentials — the same practice regularly migrates into production code.

Is CWE-798 the same as CWE-259?

CWE-259 (Use of Hard-Coded Password) is a child of CWE-798 (Use of Hard-Coded Credentials). CWE-798 is the broader class covering all credential types. CWE-259 is specifically about passwords. Most SAST tools report the finding under CWE-798 and may additionally cite CWE-259 or CWE-321 (for cryptographic keys). The remediation is identical.

Does base64-encoding a credential fix CWE-798?

No. Base64 is a reversible encoding scheme, not encryption. atob("U2VjcmV0UGFzc3dvcmQ=") decodes in milliseconds. A base64-encoded credential embedded in source code is still CWE-798. Any “obfuscation” of a hardcoded credential — ROT13, hex encoding, simple XOR — does not resolve the finding.

If credentials are in environment variables in CI/CD, is that CWE-798?

No. Credentials stored as GitHub Actions secrets, GitLab CI/CD variables, Jenkins credential store values, or Kubernetes secrets are not CWE-798 — as long as they are not also present as string literals in the source code. The CWE-798 finding resolves when the literal value is removed from source.

Can a DAST scanner find CWE-798?

Not directly. DAST tests the running application from the outside and cannot read source code. SAST tools detect CWE-798 by analyzing source code statically. Some DAST scanners can detect when an application exposes credentials in responses (e.g., an API endpoint that returns connection strings or private keys in error messages), but the primary detection mechanism for CWE-798 is SAST.


Summary

CWE-798 (Use of Hard-Coded Credentials) is a Critical or High-severity vulnerability in every major SAST tool’s classification because it requires no exploitation skill — the credential is already exposed to anyone with code access, binary decompilation capability, or access to the container image.

The complete remediation sequence:

  1. Rotate the credential immediately — assume it is already compromised
  2. Scope the exposure via git history (git log -S "credential_value")
  3. Replace the literal with an environment variable or secrets manager
  4. Clean git history with git-filter-repo
  5. Prevent recurrence with a gitleaks pre-commit hook and SAST in CI/CD
  6. Re-scan to confirm the finding is resolved

The code change itself is small — a few lines to load from environment instead of a literal. The critical steps are rotation and history cleanup, which must happen regardless of how quickly the code change is made.


Offensive360 SAST detects CWE-798 (hardcoded credentials) across 60+ languages including Java, C#, Python, JavaScript, PHP, Go, and Ruby. Run a one-time code scan for $500 to find all hardcoded credential findings in your codebase — results within 48 hours. Or book a demo to see the detection in action.

Offensive360 Security Research Team

Application Security Research

Find vulnerabilities before attackers do

Run Offensive360 SAST and DAST against your applications and get a full vulnerability report in minutes.