Hardcoded Secrets and Credentials

Hardcoded Secrets and Credentials

Hardcoded secrets in IaC templates represent one of the most dangerous yet preventable vulnerabilities. Passwords, API keys, and certificates embedded in templates are visible to anyone with repository access. Version control systems preserve these secrets in history even after removal. Automated scanning by attackers can quickly identify and exploit exposed credentials.

The pressure to quickly prototype or test functionality leads to temporary hardcoding that becomes permanent. Developers might hardcode credentials intending to replace them later but forget. Example code copied from documentation often contains placeholder credentials that make it into production. These practices create severe security vulnerabilities.

# DANGEROUS: Hardcoded secrets in Terraform

# VULNERABLE: Hardcoded database password
resource "aws_db_instance" "vulnerable" {
  engine   = "mysql"
  username = "admin"
  password = "SuperSecret123!"  # DANGER: Hardcoded password
}

# VULNERABLE: Hardcoded API key
resource "aws_lambda_function" "vulnerable" {
  function_name = "data-processor"
  
  environment {
    variables = {
      API_KEY = "sk_live_4242424242424242"  # DANGER: Hardcoded API key
      DB_PASS = "ProductionPassword123"     # DANGER: Hardcoded password
    }
  }
}

# VULNERABLE: Hardcoded SSH key
resource "aws_instance" "vulnerable" {
  ami           = "ami-12345678"
  instance_type = "t2.micro"
  
  user_data = <<-EOF
    #!/bin/bash
    echo "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQ..." >> /home/ubuntu/.ssh/authorized_keys
    echo "-----BEGIN RSA PRIVATE KEY-----" > /root/.ssh/id_rsa
    echo "MIIEpAIBAAKCAQEA..." >> /root/.ssh/id_rsa  # DANGER: Private key in user data
    echo "-----END RSA PRIVATE KEY-----" >> /root/.ssh/id_rsa
  EOF
}

# SECURE: Using Secrets Manager
resource "aws_db_instance" "secure" {
  engine   = "mysql"
  username = local.db_creds.username
  password = local.db_creds.password
}

locals {
  db_creds = jsondecode(
    data.aws_secretsmanager_secret_version.db.secret_string
  )
}

data "aws_secretsmanager_secret_version" "db" {
  secret_id = aws_secretsmanager_secret.db.id
}

resource "aws_secretsmanager_secret" "db" {
  name = "${var.environment}-db-credentials"
  
  rotation_rules {
    automatically_after_days = 30
  }
}

# SECURE: Using SSM Parameter Store for API keys
resource "aws_lambda_function" "secure" {
  function_name = "data-processor"
  
  environment {
    variables = {
      API_KEY_PARAM = aws_ssm_parameter.api_key.name
      DB_PASS_PARAM = aws_ssm_parameter.db_pass.name
    }
  }
}

resource "aws_ssm_parameter" "api_key" {
  name  = "/${var.environment}/api/key"
  type  = "SecureString"
  value = var.api_key  # Provided at runtime, not in code
}

# SECURE: Using AWS Systems Manager Session Manager instead of SSH keys
resource "aws_instance" "secure" {
  ami                    = "ami-12345678"
  instance_type         = "t2.micro"
  iam_instance_profile  = aws_iam_instance_profile.ssm.name
  
  # No SSH keys needed - access via Session Manager
  
  metadata_options {
    http_tokens = "required"  # IMDSv2 only
  }
}