This vulnerability occurs when an application stores passwords or other secrets (like API keys, database credentials) directly within the source code or in easily accessible configuration files. These secrets are then often committed to version control systems (like Git), making them visible to anyone with access to the repository, potentially including attackers. 🔑
Hard-coded credentials provide a direct path for attackers to compromise systems. If a database password is hard-coded and the code is leaked (e.g., via a public repository), attackers can gain full access to the database. Leaked API keys can lead to abuse of third-party services, resulting in high costs or data breaches.
Reference Details
CWE ID:CWE-259OWASP Top 10 (2021): A02:2021 - Cryptographic Failures (often related to key management)
Severity: High
This vulnerability is framework-agnostic but extremely common. It’s purely a developer practice issue. The fix involves externalizing secrets:
Remove the hard-coded secret from the code/config file.
Store the secret securely using environment variables, a dedicated secrets management service (like HashiCorp Vault, AWS Secrets Manager, Azure Key Vault), or encrypted configuration files.
Use environment variables (os.environ.get()) or a library like python-dotenv to load secrets from a .env file (which should not be committed to Git). For production, use environment variables provided by the deployment platform or a secrets manager.
# settings.py (Secure)import osfrom dotenv import load_dotenvload_dotenv() # Loads variables from .env fileDATABASES = { 'default': { 'ENGINE': 'django.db.backends.postgresql', 'NAME': os.environ.get('DB_NAME'), 'USER': os.environ.get('DB_USER'), # SECURE: Load password from environment variable. 'PASSWORD': os.environ.get('DB_PASSWORD'), 'HOST': os.environ.get('DB_HOST', '127.0.0.1'), 'PORT': os.environ.get('DB_PORT', '5432'), }}# .env (DO NOT COMMIT THIS FILE)# DB_NAME=mydatabase# DB_USER=myuser# DB_PASSWORD=MySuperSecretPassword123!# DB_HOST=127.0.0.1# DB_PORT=5432
# services/payment_gateway.py (Secure)import osimport requestsdef process_payment(amount, card_details): # SECURE: Load API key from environment variable. api_key = os.environ.get("PAYMENT_API_KEY") if not api_key: raise ValueError("Payment API Key not configured") headers = {'Authorization': f'Bearer {api_key}'} # ... rest of the code ...
Use automated secret scanning tools (like Codepure’s Secret Scanning!) in your CI/CD pipeline and pre-commit hooks. Manually review configuration files (settings.py, .yaml, .json) and code for hard-coded strings that look like passwords or keys. Check your Git history for accidentally committed secrets.
Vulnerable Scenario 1: Password in application.properties
# application.properties# DANGEROUS: Credentials directly in a file often committed to Git.spring.datasource.url=jdbc:postgresql://localhost:5432/mydatabasespring.datasource.username=myuserspring.datasource.password=MySuperSecretPassword123!
Use environment variables, Spring Cloud Config Server with encrypted properties, or integration with secrets management tools (like HashiCorp Vault, AWS Secrets Manager).
# application.properties (Secure - using Env Vars)# SECURE: Values will be loaded from environment variables.spring.datasource.url=${DB_URL}spring.datasource.username=${DB_USER}spring.datasource.password=${DB_PASSWORD}
// service/ThirdPartyService.java (Secure - using @Value)import org.springframework.beans.factory.annotation.Value;import org.springframework.stereotype.Service;@Servicepublic class ThirdPartyService { // SECURE: Inject API Key from environment/properties. @Value("${third.party.api.key}") private String apiKey; public void callApi() { if (apiKey == null || apiKey.isEmpty()) { throw new IllegalStateException("API Key not configured"); } // ... uses this.apiKey ... }}// application.properties (To support @Value injection)# third.party.api.key=${THIRD_PARTY_API_KEY}
Use secret scanning tools in CI/CD. Review .properties, .yml files, and Java source code (especially constants) for strings resembling credentials. Check Git history.
// appsettings.json (Secure - structure only){ "ConnectionStrings": { // SECURE: Connection string structure, actual value from Env Var or Key Vault. "DefaultConnection": "" }, "ExternalApi": { "ApiKey": "" // Structure, value from Env Var or Key Vault } // ...}
// Startup.cs or Program.cs (Secure - Loading Config)public class Startup{ public Startup(IConfiguration configuration) { Configuration = configuration; } public IConfiguration Configuration { get; } public void ConfigureServices(IServiceCollection services) { // SECURE: Reads connection string potentially overridden by Env Var or Key Vault services.AddDbContext<ApplicationDbContext>(options => options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection"))); }}// Services/ExternalApiService.cs (Secure - Inject IConfiguration)public class ExternalApiService{ private readonly HttpClient _httpClient; // API Key loaded via IConfiguration public ExternalApiService(HttpClient httpClient, IConfiguration config) { _httpClient = httpClient; // SECURE: Load key from configuration (Env Var, Key Vault, etc.) var apiKey = config["ExternalApi:ApiKey"]; if (string.IsNullOrEmpty(apiKey)) { throw new InvalidOperationException("API Key not configured"); } _httpClient.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", apiKey); } // ...}
Use secret scanning tools in CI/CD. Review appsettings.json, web.config, and C# source files (constants, static fields) for credentials. Check Git history. Utilize tools like the dotnet user-secrets manager during development.
The .env file containing production secrets is accidentally committed to Git.
# .env (DANGEROUS if committed!)DB_CONNECTION=mysqlDB_HOST=127.0.0.1DB_PORT=3306DB_DATABASE=mydatabaseDB_USERNAME=myuserDB_PASSWORD=MySuperSecretPassword123!MAIL_PASSWORD=MyAppEmailPassword# Modified example to avoid scanners:STRIPE_SECRET=sk_live_EXAMPLE_verySecretApiKeyGoesHere
Never commit your .env file. Use .env.example as a template. Load all secrets using env('SECRET_NAME'). Avoid providing sensitive defaults in config files; throw an exception if a required environment variable is missing. Use Laravel Forge, Envoyer, or platform environment variables for production.
Use secret scanning tools in CI/CD and pre-commit hooks. Ensure .env is listed in your .gitignore. Review config files (config/*.php) for hard-coded fallback credentials. Check Git history.
Use environment variables (process.env.SECRET_NAME). Libraries like dotenv can load these from a .env file for local development (don’t commit .env). Use platform environment variables or secrets management solutions in production.
Use secret scanning tools in CI/CD and pre-commit hooks. Ensure .env is in .gitignore. Review .js and .json files for hard-coded credentials. Check Git history.
Hard-coding secrets in config/database.yml, config/secrets.yml (older Rails), config/credentials.yml.enc (if the master key is leaked), or directly in Ruby code.
# config/initializers/stripe.rb# DANGEROUS: API key hard-coded.# Modified example to avoid scanners:Stripe.api_key = 'sk_live' + '_verySecretApiKeyGoesHere' # Broken up string
Use Rails encrypted credentials (config/credentials.yml.enc) and ensure the config/master.key file is never committed. Alternatively, use environment variables.
Use secret scanning tools in CI/CD. Ensure config/master.key is in .gitignore. Review .yml files and Ruby source code (initializers, constants) for hard-coded secrets. Check Git history.