Skip to main content

Free 30-min security demo Book Now

Offensive360 Offensive360
Vulnerability Research

CWE-798: Hard-Coded Credentials — What It Is, SAST Detection & Fix

CWE-798 (Use of Hard-Coded Credentials): severity, how SAST tools detect it, and exact remediation steps for Java, Python, C# & Node.js. Rotate first, then fix.

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

CWE-798 is the Common Weakness Enumeration identifier for “Use of Hard-Coded Credentials” — a vulnerability that occurs when authentication secrets (passwords, API keys, tokens, cryptographic keys) are embedded directly in source code rather than loaded from a secure external source at runtime.

CWE-798 is classified in the CWE Top 25 Most Dangerous Software Weaknesses and consistently ranks among the highest-severity findings in enterprise security assessments. Every major SAST platform — Checkmarx, Fortify, Veracode, SonarQube, and Offensive360 — flags CWE-798 as High or Critical severity.


CWE-798 at a Glance

PropertyValue
CWE IDCWE-798
NameUse of Hard-Coded Credentials
ParentCWE-344 (Use of Invariant Value in Dynamically Changing Context)
OWASP MappingA07:2021 — Identification and Authentication Failures
CVSS Base ScoreVaries; typically 7.5–9.8 (High to Critical)
CWE Top 25Yes — consistently in top 25 most dangerous software weaknesses
Common SAST query namesUse_Of_Hard_Coded_Password (Checkmarx), Password_Management_Hard_Coded (Fortify), HardcodedCredentials (Veracode)

What CWE-798 Covers

CWE-798 covers any secret that is embedded as a literal value in source code. This includes:

Passwords and passphrases:

private static final String DB_PASSWORD = "S3cur3P@ssw0rd!";

API keys and tokens:

api_key = "sk-live-abc123def456xyz789"
stripe_secret = "sk_live_51Habcdefghijklmnop"

Connection strings with embedded credentials:

const connectionString = "mongodb://admin:[email protected]:27017/app";

Private cryptographic keys:

$private_key = "-----BEGIN RSA PRIVATE KEY-----\nMIIEowIBAAK...";

Base64-encoded credentials (encoding does not constitute encryption):

// STILL CWE-798 — base64 is trivially reversible
string encodedCreds = "YWRtaW46cGFzc3dvcmQxMjM="; // admin:password123

Default credentials in firmware or applications:

const DefaultAdminPassword = "admin" // Ships with every deployment

Why CWE-798 Is Rated Critical

SAST tools rate CWE-798 as High or Critical because the exploit path is trivially short:

1. No Skills Required to Exploit

Once the credential is exposed, exploitation requires only the credential itself — no vulnerability chaining, no exploitation technique. Any person with access to the code (or a repository leak, or a decompiled binary) can immediately use the credential.

2. Git History Retains Secrets Permanently

# Anyone with repository access can recover the credential instantly
git log -S "S3cur3P@ssw0rd" --all --oneline
git log --all -p | grep -A3 -B3 "password"

Removing the credential in a later commit does not remove it from git history. The credential remains accessible via git log indefinitely.

3. Automated Scanning Finds Exposed Credentials Within Minutes

When a repository is accidentally made public, automated scanners (run by both security researchers and malicious actors) scan GitHub, GitLab, and Bitbucket for credential patterns within minutes of publication.

4. Every Developer With Repository Access Has the Credential

Past and present developers, contractors, third-party auditors, and CI/CD systems — anyone who has ever cloned the repository has the credential. This is true even after access is revoked at the platform level.

5. Rotation Requires Redeployment

A hardcoded credential cannot be rotated without modifying code and redeploying. If the credential is compromised, the window of exposure is the entire period the code has been running — potentially years.


How Attackers Exploit CWE-798

Path 1: Repository Access → Credential Extraction

# Direct extraction from source code
grep -r "password\|api_key\|secret\|token\|credential" . --include="*.java" --include="*.py"

# Using tools designed for this purpose
trufflehog git file://./my-repo
gitleaks detect --source . --verbose
git-secrets --scan -r .

Path 2: Binary / APK Reverse Engineering

Android APKs, compiled Java JARs, and native binaries can be decompiled or disassembled to recover hardcoded strings:

# Android APK
jadx -d output/ target.apk && grep -r "api_key\|password\|secret" output/

# Java JAR
javap -c target.jar | strings | grep -i "password\|key\|secret"

# Native binary
strings ./binary | grep -E "[a-zA-Z0-9]{20,}"

Path 3: Docker Image Layer Inspection

Credentials set via ENV instructions in Dockerfiles or embedded in image layers are recoverable:

# Inspect Docker image layers for credentials
docker history --no-trunc myapp:latest
docker inspect myapp:latest | python3 -m json.tool | grep -i "env\|password"
docker run --rm -it myapp:latest env | grep -i "pass\|key\|secret"

CWE-798 in Practice: Language-Specific Patterns

Java / Spring Boot

Vulnerable:

// CWE-798 — credentials in source code
@Configuration
public class DatabaseConfig {
    
    @Bean
    public DataSource dataSource() {
        HikariDataSource ds = new HikariDataSource();
        ds.setJdbcUrl("jdbc:postgresql://prod-db.internal:5432/app");
        ds.setUsername("dbadmin");
        ds.setPassword("Pr0d_DB_P@ssw0rd!"); // CWE-798
        return ds;
    }
}

Fixed:

// Credentials from environment — resolves CWE-798
@Configuration
public class DatabaseConfig {

    @Value("${DB_URL}")
    private String dbUrl;

    @Value("${DB_USERNAME}")
    private String dbUsername;

    @Value("${DB_PASSWORD}")
    private String dbPassword;

    @Bean
    public DataSource dataSource() {
        HikariDataSource ds = new HikariDataSource();
        ds.setJdbcUrl(dbUrl);
        ds.setUsername(dbUsername);
        ds.setPassword(dbPassword); // Value injected at runtime from environment
        return ds;
    }
}
# application.properties — values set via environment variables
spring.datasource.url=${DB_URL}
spring.datasource.username=${DB_USERNAME}
spring.datasource.password=${DB_PASSWORD}

Python

Vulnerable:

# CWE-798 — hardcoded credentials
import psycopg2

def get_db_connection():
    return psycopg2.connect(
        host="prod-db.internal",
        database="app",
        user="admin",
        password="Pr0d_DB_P@ss!"  # CWE-798
    )

STRIPE_SECRET = "sk_live_51HabcXYZ..."  # CWE-798
SENDGRID_KEY = "SG.abcdefghijklmnop"   # CWE-798

Fixed:

import os
import psycopg2
import boto3
import json

# Option 1: Environment variables (minimum fix — resolves CWE-798)
def get_db_connection():
    return psycopg2.connect(
        host=os.environ["DB_HOST"],
        database=os.environ["DB_NAME"],
        user=os.environ["DB_USER"],
        password=os.environ["DB_PASSWORD"]
    )

# Option 2: AWS Secrets Manager (recommended for production)
def get_db_credentials() -> dict:
    client = boto3.client("secretsmanager", region_name="us-east-1")
    response = client.get_secret_value(SecretId="prod/myapp/database")
    return json.loads(response["SecretString"])

C# / .NET

Vulnerable:

// CWE-798 — connection string hardcoded
public class AppDbContext : DbContext
{
    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    {
        optionsBuilder.UseNpgsql(
            "Host=prod-db;Database=app;Username=admin;Password=Pr0d_P@ss!" // CWE-798
        );
    }
}

// Also CWE-798:
private readonly string _apiKey = "sk-live-abc123def456"; // CWE-798

Fixed:

// Credentials via IConfiguration (environment variables or secrets manager)
public class AppDbContext : DbContext
{
    private readonly IConfiguration _configuration;

    public AppDbContext(IConfiguration configuration)
    {
        _configuration = configuration;
    }

    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    {
        // Value comes from environment variable or Azure Key Vault
        optionsBuilder.UseNpgsql(_configuration.GetConnectionString("DefaultConnection"));
    }
}
// appsettings.json — connection string resolved from environment at runtime
{
  "ConnectionStrings": {
    "DefaultConnection": "" 
  }
}
# Runtime: set environment variable (Kubernetes secret, Azure App Service setting, etc.)
export ConnectionStrings__DefaultConnection="Host=prod-db;Database=app;Username=admin;Password=${DB_PASSWORD}"

Node.js / JavaScript

Vulnerable:

// CWE-798 — credentials in source code
const mysql = require('mysql2');

const pool = mysql.createPool({
  host: 'prod-db.internal',
  user: 'root',
  password: 'MySuperSecret123!', // CWE-798
  database: 'app'
});

const AWS_ACCESS_KEY = "AKIAIOSFODNN7EXAMPLE"; // CWE-798
const AWS_SECRET_KEY = "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"; // CWE-798

Fixed:

require('dotenv').config(); // .env file in development; real env vars in production

const mysql = require('mysql2');

const pool = mysql.createPool({
  host: process.env.DB_HOST,
  user: process.env.DB_USER,
  password: process.env.DB_PASSWORD,
  database: process.env.DB_NAME
});
# .env (development only — never commit)
DB_HOST=localhost
DB_USER=app_user
DB_PASSWORD=local_dev_password
DB_NAME=app_dev

# .env.example (commit this — shows required vars, no values)
DB_HOST=
DB_USER=
DB_PASSWORD=
DB_NAME=

How SAST Tools Detect CWE-798

Checkmarx

Query name: Use_Of_Hard_Coded_Password (CxSAST) / Hardcoded_Credentials (Checkmarx One)

Checkmarx detects CWE-798 through data flow analysis:

  1. Source identification: String literals that match credential patterns (variable names containing password, passwd, pwd, secret, apikey, token, key)
  2. Sink identification: Security-sensitive method calls — DriverManager.getConnection(), new SecretKeySpec(), HttpBasicAuthentication(), etc.
  3. Taint path: The literal value flows directly from the constant declaration to the sensitive sink

Typical Checkmarx CWE-798 finding:

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

Common Checkmarx CWE-798 false positives:

  • Test fixtures: "test_password" in unit test files
  • Placeholder strings: "YOUR_API_KEY_HERE", "REPLACE_ME"
  • Non-sensitive variable names accidentally matching: password_length = 12

Fortify SCA

Category name: Password Management: Hard-Coded Password

Fortify uses semantic analysis to detect string literals flowing to authentication APIs. It also specifically detects:

  • Credentials in Spring @Value("hardcoded_value") annotations
  • Credentials in JDBC URL strings: jdbc:postgresql://user:pass@host/db
  • Cryptographic keys in new SecretKeySpec(new byte[]{...}) calls

Offensive360

Rule name: HardcodedCredentials

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

  • String literals with credential-pattern variable names
  • Embedded connection strings with username:password format
  • Base64-encoded values flowing to authentication methods
  • Cryptographic key material as byte array literals
  • Dockerfile ENV instructions and docker-compose credentials

Findings include the file path, line number, credential type (password/API key/connection string/private key), and a direct link to the remediation guidance.


Remediation Checklist for CWE-798

When you receive a CWE-798 finding from a SAST scanner, follow this sequence:

Step 1: Rotate the Credential Immediately

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 cryptographic key
  • Update all systems that use the credential to use the new value

Do not wait until after the code fix to rotate — the credential should be considered burned the moment it’s identified.

Step 2: Check Git History for Scope

# Check if the credential exists in git history
git log --all -p -S "the_credential_value" | head -50

# Check how far back it goes
git log --all --oneline -- path/to/affected/file

If the credential appears in git history, history cleanup is required regardless of when it was introduced.

Step 3: Replace With Environment Variable or Secrets Manager

Move the credential to the appropriate external source:

  • Development: .env file (added to .gitignore)
  • CI/CD: Environment secrets (GitHub Actions secrets, GitLab CI variables, Jenkins credentials store)
  • Production: Dedicated secrets manager (AWS Secrets Manager, HashiCorp Vault, Azure Key Vault, GCP Secret Manager)

Step 4: Clean Git History

# Install git-filter-repo
pip install git-filter-repo

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

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

After cleaning history, all collaborators must delete local clones and re-clone. Any cached copies in CI/CD systems must also be invalidated.

Step 5: Add Prevention Controls

Prevent the same class of issue from recurring:

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

Step 6: Re-scan to Confirm Resolution

After making code changes, run the SAST scan again to confirm the finding is resolved. Most SAST tools show the CWE-798 finding as resolved once the string literal is removed from the source file.


Secrets Management Solutions

Moving credentials from source code to a secrets manager is the production-grade fix for CWE-798:

SolutionBest forAutomatic Rotation
AWS Secrets ManagerAWS-hosted applications✅ Yes (built-in rotation lambdas)
HashiCorp VaultMulti-cloud / on-premise✅ Yes (dynamic secrets)
Azure Key VaultAzure-hosted applications✅ Yes (managed rotation)
GCP Secret ManagerGCP-hosted applications✅ Yes
DopplerDeveloper-friendly SaaS✅ Yes
1Password Secrets AutomationSmall teamsManual
Environment variablesAny deploymentManual (minimum viable fix)

Environment variables are the minimum fix that resolves the SAST finding. For regulated environments (PCI-DSS, HIPAA, SOC 2), a dedicated secrets manager with audit logging and automatic rotation is the appropriate solution.


CWE-798 in Regulatory and Compliance Frameworks

CWE-798 maps directly to requirements in major compliance frameworks:

FrameworkRequirement
PCI-DSS 4.0Requirement 8.6.1 — No hardcoded accounts/passwords for application/system accounts
OWASP ASVS 4.0V2.10.1 — No hardcoded passwords in the codebase
SOC 2CC6.1 — Logical and physical access controls (hardcoded credentials violate the principle)
ISO 27001A.9.4.3 — Password management system (credentials must be managed, not hardcoded)
NIST SP 800-53IA-5 (Authenticator Management) — credentials must be changeable

A CWE-798 finding in a pre-audit SAST scan is a compliance blocker in all of the above frameworks. Most compliance auditors will flag it as a material finding requiring immediate remediation.


Frequently Asked Questions

Does CWE-798 apply to test code?

SAST tools typically flag CWE-798 in test files as well as production code. Test credentials in unit tests and integration tests should ideally be replaced with environment variables or placeholder values — not because test-only credentials pose the same risk as production credentials, but because the same developers write test and production code, and the habit of hardcoding “just for testing” regularly migrates to production code.

For legitimate test credentials where the SAST finding is a false positive, document the suppression with a specific reason.

What’s the difference between CWE-798 and CWE-259?

  • CWE-798 (Use of Hard-Coded Credentials): General class covering any hardcoded secret
  • CWE-259 (Use of Hard-Coded Password): Specific subtype of CWE-798, covering specifically passwords (as opposed to API keys, tokens, or cryptographic keys)

Most SAST tools report hardcoded passwords as CWE-798 and may additionally cite CWE-259. The remediation is identical.

Is base64-encoding a credential sufficient to resolve CWE-798?

No. Base64 encoding is not encryption — it is a reversible encoding scheme. A base64-encoded credential is still a hardcoded credential (CWE-798). atob("dXNlcjpwYXNzd29yZA==") takes milliseconds to decode.

My CI/CD pipeline has the credential in an environment variable — is that still CWE-798?

No. If the credential is in a CI/CD environment variable (GitHub Actions secret, GitLab CI variable, Jenkins credential store) and is not present as a string literal in the source code, it is not CWE-798. The finding resolves when the literal value is removed from source code.


Summary

CWE-798 (Use of Hard-Coded Credentials) is one of the most consistently exploitable vulnerability classes in enterprise software. It requires no exploitation skill — the credential is already exposed. The fix is straightforward: move every authentication secret from source code to an environment variable or secrets manager, rotate the exposed credential immediately, and clean git history.

The five-step remediation sequence — rotate → scope history → remove from code → clean history → add prevention controls — resolves both the immediate risk and the structural conditions that allow CWE-798 to recur.


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 identify all hardcoded credential findings in your codebase — results within 48 hours. Or book a demo to see the full platform.

Offensive360 Security Research Team

Application Security Research

Updated August 1, 2026

Find vulnerabilities before attackers do

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