skills/igbuend/grimbard/insecure-defaults-anti-pattern

insecure-defaults-anti-pattern

SKILL.md

Insecure Defaults Anti-Pattern

Severity: Critical

Summary

Insecure defaults occur when applications continue operating with weak or default values when required configuration is missing. Unlike hardcoded secrets (which are always present), insecure defaults create fail-open conditions where missing environment variables cause the application to silently use unsafe fallback values. This is particularly dangerous because the vulnerability only manifests in misconfigured deployments.

The Anti-Pattern

Never provide fallback values for security-critical configuration. Applications should fail immediately (fail-secure) when required secrets or security settings are missing.

Key Distinction

Pattern Behavior Risk
Fail-open (BAD) Uses default when config missing Silent security bypass
Fail-secure (GOOD) Crashes when config missing Deployment fails safely

BAD Code Examples

# VULNERABLE: Fail-open - application runs with weak defaults
import os
import jwt

# 1. Default secret when environment variable is missing
SECRET_KEY = os.environ.get("SECRET_KEY", "default-secret-change-me")

# 2. Debug mode defaults to enabled
DEBUG = os.environ.get("DEBUG", "true").lower() == "true"

# 3. Weak algorithm fallback
JWT_ALGORITHM = os.environ.get("JWT_ALGORITHM", "HS256")  # Should require RS256

def create_token(user_id):
    # Runs with weak secret if SECRET_KEY not set
    return jwt.encode({"user_id": user_id}, SECRET_KEY, algorithm=JWT_ALGORITHM)

def verify_token(token):
    # Attacker can forge tokens using "default-secret-change-me"
    return jwt.decode(token, SECRET_KEY, algorithms=[JWT_ALGORITHM])
// VULNERABLE: Node.js fail-open patterns
const express = require('express');
const session = require('express-session');

const app = express();

// 1. Session secret with insecure default
app.use(session({
  secret: process.env.SESSION_SECRET || 'keyboard cat',  // Fail-open!
  resave: false,
  saveUninitialized: true
}));

// 2. CORS defaults to permissive
const corsOrigin = process.env.CORS_ORIGIN || '*';  // Allows all origins!

// 3. Rate limiting disabled by default
const rateLimit = process.env.RATE_LIMIT || 0;  // 0 = unlimited

GOOD Code Examples

# SECURE: Fail-secure - application crashes if config missing
import os
import sys
import jwt

def get_required_env(name):
    """Get required environment variable or exit."""
    value = os.environ.get(name)
    if not value:
        sys.exit(f"FATAL: Required environment variable {name} is not set")
    return value

# 1. No default - must be configured
SECRET_KEY = get_required_env("SECRET_KEY")

# 2. Debug defaults to disabled (safe default)
DEBUG = os.environ.get("DEBUG", "false").lower() == "true"

# 3. Validate algorithm is secure
JWT_ALGORITHM = os.environ.get("JWT_ALGORITHM", "RS256")
if JWT_ALGORITHM not in ["RS256", "RS384", "RS512", "ES256", "ES384", "ES512"]:
    sys.exit(f"FATAL: Insecure JWT algorithm: {JWT_ALGORITHM}")

def create_token(user_id):
    return jwt.encode({"user_id": user_id}, SECRET_KEY, algorithm=JWT_ALGORITHM)
// SECURE: Node.js fail-secure patterns
const express = require('express');
const session = require('express-session');

function requireEnv(name) {
  const value = process.env[name];
  if (!value) {
    console.error(`FATAL: Required environment variable ${name} is not set`);
    process.exit(1);
  }
  return value;
}

const app = express();

// 1. Session secret required - no default
app.use(session({
  secret: requireEnv('SESSION_SECRET'),
  resave: false,
  saveUninitialized: false,  // Also secure default
  cookie: { secure: true }   // Require HTTPS
}));

// 2. CORS must be explicitly configured
const corsOrigin = requireEnv('CORS_ORIGIN');
if (corsOrigin === '*') {
  console.error('FATAL: CORS_ORIGIN cannot be wildcard in production');
  process.exit(1);
}

// 3. Rate limiting with secure default
const rateLimit = parseInt(process.env.RATE_LIMIT || '100', 10);

Language-Specific Examples

Go:

// VULNERABLE: Fail-open defaults
func getConfig() Config {
    return Config{
        // Default secret if not set
        JWTSecret:  getEnvOrDefault("JWT_SECRET", "development-secret"),
        // Debug enabled by default
        Debug:      getEnvOrDefault("DEBUG", "true") == "true",
        // Permissive CORS
        CORSOrigin: getEnvOrDefault("CORS_ORIGIN", "*"),
    }
}
// SECURE: Fail-secure - panic on missing required config
func getConfig() Config {
    jwtSecret := os.Getenv("JWT_SECRET")
    if jwtSecret == "" {
        log.Fatal("FATAL: JWT_SECRET environment variable required")
    }

    corsOrigin := os.Getenv("CORS_ORIGIN")
    if corsOrigin == "" || corsOrigin == "*" {
        log.Fatal("FATAL: CORS_ORIGIN must be explicitly set (not wildcard)")
    }

    return Config{
        JWTSecret:  jwtSecret,
        Debug:      os.Getenv("DEBUG") == "true",  // Defaults to false
        CORSOrigin: corsOrigin,
    }
}

Java/Spring Boot:

// VULNERABLE: application.properties with insecure defaults
// jwt.secret=${JWT_SECRET:default-secret-do-not-use}
// cors.allowed-origins=${CORS_ORIGINS:*}
// debug.enabled=${DEBUG:true}
// SECURE: Require configuration, no insecure defaults
@Configuration
public class SecurityConfig {

    @Value("${jwt.secret}")  // No default - fails if missing
    private String jwtSecret;

    @Value("${cors.allowed-origins}")  // No default
    private String corsOrigins;

    @PostConstruct
    public void validateConfig() {
        if (jwtSecret == null || jwtSecret.length() < 32) {
            throw new IllegalStateException("jwt.secret must be at least 32 characters");
        }
        if ("*".equals(corsOrigins)) {
            throw new IllegalStateException("cors.allowed-origins cannot be wildcard");
        }
    }
}

Detection

Search for fallback patterns in configuration code:

# Python: os.environ.get with default values for secrets
rg 'environ\.get\([^)]+,\s*["\'][^"\']+["\']' --type py

# JavaScript: process.env with || fallback
rg 'process\.env\.\w+\s*\|\|' --type js --type ts

# Go: getEnvOrDefault patterns
rg 'getEnv.*Default|Getenv.*""' --type go

# Generic: Common insecure default strings
rg -i '(secret|key|password|token).*default|change.?me|keyboard.?cat|development'

What to Ignore (Not Vulnerabilities)

  • Test directories and spec files
  • Example/sample/template files
  • Development-only configurations (docker-compose.dev.yml)
  • Documentation and README files
  • Build-time placeholders replaced during deployment
  • Fail-secure patterns that crash on missing config

Prevention

  • Never provide defaults for secrets - API keys, passwords, signing keys must be explicitly configured
  • Fail-secure on startup - Crash immediately if required configuration is missing
  • Default security settings to restrictive - Debug=false, CORS=specific origins, rate limiting=enabled
  • Validate configuration at startup - Check for weak values, not just missing values
  • Use schema validation - Validate all config against expected types and constraints
  • Separate dev/prod configs - Never share configuration between environments

Related Anti-Patterns

References

Weekly Installs
3
GitHub Stars
4
First Seen
Feb 19, 2026
Installed on
openclaw3
claude-code3
replit3
codex3
kiro-cli3
kimi-cli3