Lesson 06 Secrets Management & Cryptographic Hygiene
Theme:
Lesson 06 · Slide 1

Secrets Management & Cryptographic Hygiene

Learn to find, secure, and manage secrets across your entire stack — and apply AI-driven analysis to detect weak cryptographic implementations before attackers do.

Secrets Scanning Vault TLS/SSL Key Rotation AI-Assisted Audit CTF Blue Team

⚠ Educational Use Only

Secrets scanning and cryptographic analysis techniques are for authorized security auditing and CTF competitions. Never exploit discovered credentials without authorization.

Lesson 06 · Slide 2

Why Secrets Management Matters

Leaked secrets are the number-one cause of preventable breaches. A single exposed API key can compromise an entire organization in minutes.

Notes: Emphasize that secrets management is not optional — it is a fundamental security control, not an afterthought.
Lesson 06 · Slide 3

Real Breach Case Studies

Uber (2022)

Hardcoded admin credentials in a PowerShell script on a network share. Attacker gained access to Uber's AWS, GCP, and internal Slack.

Samsung (2022)

Lapsus$ group extracted 190 GB of source code including private keys, API keys, and secret keys for Samsung Knox authentication.

Toyota (2022)

Access key to customer data server was accidentally committed to a public GitHub repo — exposed for 5 years before discovery.

CircleCI (2023)

Compromised engineer laptop led to stolen session tokens. All customer secrets stored in CircleCI needed rotation.

Lesson 06 · Slide 4

Anatomy of a Leaked Secret

Here is a typical .env file accidentally committed to a public repository:

# .env — DO NOT COMMIT THIS FILE
DATABASE_URL=postgresql://admin:S3cur3P@ss!@prod-db.company.io:5432/maindb
AWS_ACCESS_KEY_ID=AKIAIOSFODNN7EXAMPLE
AWS_SECRET_ACCESS_KEY=wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY
STRIPE_SECRET_KEY=sk_live_4eC39HqLyjWDarjtT1zdp7dc
OPENAI_API_KEY=sk-proj-abc123def456ghi789jkl012mno345pqr678
GITHUB_TOKEN=ghp_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
JWT_SECRET=my-super-secret-jwt-key-dont-tell-anyone
SENDGRID_API_KEY=SG.xxxxxxxxxxxxxxxxxxxxxxxx.yyyyyyyyyyyyyyyy

Every line is a potential breach vector

Database credentials, cloud provider keys, payment processor secrets, AI API keys, and authentication tokens — all in plain text, all one git push away from public exposure.

Lesson 06 · Slide 5

Secret Sprawl: Where Secrets Hide

Secrets do not stay in one place. They spread across your infrastructure like weeds.

LocationRisk LevelExample
Source codeCriticalHardcoded connection strings
Git historyCriticalRemoved from HEAD but still in commits
.env filesHighMissing .gitignore entry
CI/CD pipelinesHighGitHub Actions secrets in logs
Container imagesHighBaked into Docker layers
Config filesMediumapplication.yml with passwords
Wikis / docsMediumShared credentials in Confluence
Chat / emailMediumAPI keys shared via Slack DMs
Lesson 06 · Slide 6

Hardcoded Credentials in Code

The most dangerous pattern — secrets embedded directly in application source:

# BAD: Hardcoded credentials in Python
import boto3
import requests

# Database connection with embedded password
conn = psycopg2.connect(
    host="prod-db.internal",
    user="admin",
    password="Pr0d_P@ssw0rd_2024!",  # NEVER DO THIS
    database="customers"
)

# AWS client with hardcoded keys
s3 = boto3.client(
    "s3",
    aws_access_key_id="AKIAIOSFODNN7EXAMPLE",
    aws_secret_access_key="wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"
)

# API key in header
resp = requests.get(
    "https://api.openai.com/v1/models",
    headers={"Authorization": "Bearer sk-proj-LIVE_KEY_HERE"}
)

Why this persists

Developers hardcode secrets during prototyping, then forget to remove them. Code review catches some, but git history preserves all.

Lesson 06 · Slide 7

Secrets in CI/CD Pipelines

CI/CD pipelines are a major attack surface for secret leakage:

# BAD: GitHub Actions workflow leaking secrets
name: Deploy
on: push
jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - name: Deploy to production
        run: |
          # Secret printed to logs via debug output
          echo "Deploying with key: ${{ secrets.AWS_SECRET_KEY }}"
          # Secret leaked through environment variable dump
          env
          # Secret baked into Docker image layer
          docker build --build-arg API_KEY=${{ secrets.API_KEY }} -t app .
# GOOD: Secure secret handling in GitHub Actions
name: Deploy
on: push
jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - name: Configure AWS Credentials
        uses: aws-actions/configure-aws-credentials@v4
        with:
          role-to-assume: arn:aws:iam::123456789:role/deploy-role
          aws-region: us-east-1
      - name: Deploy
        run: |
          # Use OIDC — no long-lived secrets at all
          aws ecs update-service --cluster prod --service app
Lesson 06 · Slide 8

Secrets in Docker Images

Docker layer caching preserves secrets even if you delete them in later layers:

# BAD: Secret persists in intermediate layer
FROM python:3.11
COPY .env /app/.env
RUN pip install -r requirements.txt
RUN python setup.py --config /app/.env
RUN rm /app/.env   # TOO LATE — .env is in a previous layer!
# GOOD: Use multi-stage builds and runtime secrets
FROM python:3.11 AS builder
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

FROM python:3.11-slim
COPY --from=builder /usr/local/lib/python3.11/site-packages /usr/local/lib/python3.11/site-packages
COPY app/ /app/
# Secrets injected at runtime via environment or mounted volumes
CMD ["python", "/app/main.py"]

Extraction is trivial

docker history --no-trunc and docker save | tar -x can recover secrets from any layer in the image.

Lesson 06 · Slide 9

Secret Scanning Tools

ToolTypeStrengths
TruffleHogGit history scannerScans entire git history, high-entropy detection, verified secrets
git-secretsPre-commit hookPrevents commits containing secrets, AWS-focused patterns
GitleaksStatic analysisFast, configurable rules, CI/CD integration
detect-secretsBaseline scannerYelp's tool, maintains baseline of known secrets
GitHub Secret ScanningPlatform nativeAutomatic alerts, partner program for key revocation
GitGuardianSaaS platformReal-time monitoring, remediation workflows
Notes: No single tool catches everything. A layered approach — pre-commit hooks + CI scanning + monitoring — provides the best coverage.
Lesson 06 · Slide 10

TruffleHog in Action

TruffleHog scans git history for high-entropy strings and known secret patterns:

# Install TruffleHog
pip install trufflehog3
# or use the Go version
brew install trufflehog

# Scan a local repository (entire git history)
trufflehog git file:///path/to/repo --only-verified

# Scan a GitHub org
trufflehog github --org=mycompany --only-verified

# Scan with JSON output for CI integration
trufflehog git file:///path/to/repo --json | jq '.SourceMetadata'

# Example output:
# Found verified result
# Detector: AWS
# Raw: AKIAIOSFODNN7EXAMPLE
# File: config/database.yml
# Commit: a1b2c3d
# Email: dev@company.com
# Repository: git@github.com:company/backend.git

Verified vs. Unverified

TruffleHog can actively verify if discovered credentials are still valid by making test API calls. The --only-verified flag reduces false positives dramatically.

Lesson 06 · Slide 11

git-secrets: Prevent Before You Push

The best secret leak is the one that never happens. Pre-commit hooks stop secrets at the source:

# Install git-secrets
brew install git-secrets

# Initialize in your repository
cd /path/to/repo
git secrets --install

# Register AWS patterns
git secrets --register-aws

# Add custom patterns
git secrets --add 'sk_live_[a-zA-Z0-9]{24}'        # Stripe live keys
git secrets --add 'sk-proj-[a-zA-Z0-9]{48}'         # OpenAI keys
git secrets --add 'ghp_[a-zA-Z0-9]{36}'             # GitHub PATs
git secrets --add 'password\s*=\s*.+'                # Generic password assignments

# Test — this commit will be BLOCKED:
echo 'API_KEY=sk_live_4eC39HqLyjWDarjtT1zdp7dc' >> config.py
git add config.py
git commit -m "add config"
# ERROR: Matched one or more prohibited patterns
# .pre-commit-config.yaml — team-wide enforcement
repos:
  - repo: https://github.com/awslabs/git-secrets
    rev: master
    hooks:
      - id: git-secrets
Lesson 06 · Slide 12

Gitleaks: Configurable Secret Detection

# Run Gitleaks against a repo
gitleaks detect --source /path/to/repo --verbose

# Scan only staged changes (pre-commit)
gitleaks protect --staged --verbose

# CI/CD mode with SARIF output
gitleaks detect --source . --report-format sarif --report-path gitleaks.sarif
# .gitleaks.toml — custom rules
title = "Custom Gitleaks Config"

[[rules]]
id = "internal-api-key"
description = "Internal API Key"
regex = '''INTERNAL_API_[A-Z]+=\S+'''
tags = ["internal", "api"]

[[rules]]
id = "jwt-secret"
description = "JWT Secret"
regex = '''(?i)(jwt[_-]?secret|jwt[_-]?key)\s*[=:]\s*\S+'''
tags = ["auth", "jwt"]

[allowlist]
paths = [
  '''test/.*''',
  '''.*_test\.go''',
  '''.*\.example'''
]
Lesson 06 · Slide 13

Using AI to Scan for Leaked Secrets

Traditional regex-based scanners miss context. AI models can understand intent and detect secrets that patterns cannot:

import anthropic

client = anthropic.Anthropic()

def ai_secret_scan(code_snippet: str) -> dict:
    """Use Claude to analyze code for potential secret leakage."""
    response = client.messages.create(
        model="claude-sonnet-4-20250514",
        max_tokens=1024,
        messages=[{
            "role": "user",
            "content": f"""Analyze this code for leaked secrets, hardcoded credentials,
or sensitive data exposure. For each finding, provide:
1. The secret type (API key, password, token, etc.)
2. The exact line or value
3. Severity (critical/high/medium/low)
4. Recommended remediation

Code to analyze:
```
{code_snippet}
```"""
        }]
    )
    return response.content[0].text

AI advantages over regex

AI can detect obfuscated secrets, base64-encoded credentials, secrets split across variables, and contextually sensitive values (e.g., "this looks like a real key, not a placeholder").

Lesson 06 · Slide 14

AI Contextual Analysis: Beyond Pattern Matching

AI can distinguish real secrets from test fixtures, examples, and documentation:

SCAN_PROMPT = """You are a security auditor. Analyze this file for secrets.
Classify each finding as:
- TRUE_POSITIVE: Real secret that needs remediation
- FALSE_POSITIVE: Test data, example, or placeholder
- SUSPICIOUS: Needs human review

Consider:
- Is this a test file? Check filename and content patterns.
- Does the value look like a real key or a placeholder (e.g., 'xxx', 'your-key-here')?
- Is the entropy high enough to be a real secret?
- Is this in a comment explaining what NOT to do?

File: {filename}
Content:
```
{content}
```"""

# This catches what regex misses:
# - connection_string = base64.b64decode("cG9zdGdyZXM6Ly9hZG1pbjpQQHNzdzByZA==")
# - password = "".join(["P","a","s","s","w","0","r","d"])
# - config["key"] = os.environ.get("API_KEY", "sk-fallback-key-abc123")
Lesson 06 · Slide 15

HashiCorp Vault: Centralized Secret Management

Vault provides a single source of truth for secrets with access control, audit logging, and automatic rotation:

# Start Vault in dev mode (for learning only)
vault server -dev

# Store a secret
vault kv put secret/myapp/database \
    username="app_user" \
    password="$(openssl rand -base64 32)" \
    host="prod-db.internal"

# Read a secret
vault kv get secret/myapp/database

# Read specific field
vault kv get -field=password secret/myapp/database
# Vault policy — principle of least privilege
path "secret/data/myapp/*" {
  capabilities = ["read"]
}

path "secret/data/myapp/database" {
  capabilities = ["read"]
  allowed_parameters = {
    "version" = []
  }
}

# Deny access to admin secrets
path "secret/data/admin/*" {
  capabilities = ["deny"]
}
Lesson 06 · Slide 16

Vault Dynamic Secrets

Dynamic secrets are generated on-demand and automatically revoked after a TTL. No more shared, long-lived credentials:

# Enable database secrets engine
vault secrets enable database

# Configure PostgreSQL connection
vault write database/config/mydb \
    plugin_name=postgresql-database-plugin \
    connection_url="postgresql://{{username}}:{{password}}@db:5432/app" \
    allowed_roles="app-readonly" \
    username="vault_admin" \
    password="vault_admin_pass"

# Create a role with TTL
vault write database/roles/app-readonly \
    db_name=mydb \
    creation_statements="CREATE ROLE \"{{name}}\" WITH LOGIN PASSWORD '{{password}}' \
        VALID UNTIL '{{expiration}}'; GRANT SELECT ON ALL TABLES IN SCHEMA public TO \"{{name}}\";" \
    default_ttl="1h" \
    max_ttl="24h"

# Generate dynamic credentials (unique per request, auto-expires)
vault read database/creds/app-readonly
# Key             Value
# lease_id        database/creds/app-readonly/abc123
# lease_duration  1h
# username        v-app-readonly-xK4r2s
# password        A1b2-C3d4-E5f6-G7h8
Lesson 06 · Slide 17

AWS Secrets Manager & SOPS

AWS Secrets Manager

import boto3
import json

client = boto3.client("secretsmanager")

# Retrieve secret at runtime
response = client.get_secret_value(
    SecretId="prod/myapp/database"
)
secret = json.loads(
    response["SecretString"]
)
db_password = secret["password"]

# Enable automatic rotation
client.rotate_secret(
    SecretId="prod/myapp/database",
    RotationLambdaARN="arn:aws:lambda:...",
    RotationRules={
        "AutomaticallyAfterDays": 30
    }
)

Mozilla SOPS

# Encrypt a config file
sops --encrypt \
  --kms arn:aws:kms:us-east-1:123:key/abc \
  secrets.yaml > secrets.enc.yaml

# Encrypted file (safe to commit!)
# myapp:
#   db_password: ENC[AES256_GCM,data:...,type:str]
#   api_key: ENC[AES256_GCM,data:...,type:str]

# Decrypt at deploy time
sops --decrypt secrets.enc.yaml

# Edit encrypted file in-place
sops secrets.enc.yaml

SOPS encrypts values but preserves keys, making diffs readable in git.

Lesson 06 · Slide 18

Cryptographic Best Practices: Algorithm Selection

Use CaseRecommendedAvoid
Symmetric encryptionAES-256-GCM, ChaCha20-Poly1305DES, 3DES, RC4, AES-ECB
Password hashingArgon2id, bcrypt, scryptMD5, SHA-1, SHA-256 (unsalted)
Digital signaturesEd25519, ECDSA (P-256), RSA-4096RSA-1024, DSA
Key exchangeX25519, ECDH (P-256)DH-1024, static RSA
Hashing (non-password)SHA-256, SHA-3, BLAKE3MD5, SHA-1
Random generationsecrets module, /dev/urandomrandom module, Math.random()

The golden rule

Never implement your own cryptographic algorithms. Use well-tested libraries: cryptography (Python), libsodium, or platform-provided APIs.

Lesson 06 · Slide 19

Common Cryptographic Mistakes

# BAD: Using ECB mode (patterns visible in ciphertext)
from Crypto.Cipher import AES
cipher = AES.new(key, AES.MODE_ECB)  # NEVER use ECB

# BAD: Using MD5 for password hashing
import hashlib
password_hash = hashlib.md5(password.encode()).hexdigest()

# BAD: Predictable random for tokens
import random
token = ''.join(random.choices('abcdef0123456789', k=32))

# BAD: Hardcoded IV / nonce reuse
cipher = AES.new(key, AES.MODE_GCM, nonce=b'\x00' * 12)  # Static nonce!
# GOOD: Proper implementations
from cryptography.fernet import Fernet
key = Fernet.generate_key()
f = Fernet(key)
ciphertext = f.encrypt(b"sensitive data")

# GOOD: Password hashing with Argon2
from argon2 import PasswordHasher
ph = PasswordHasher(time_cost=3, memory_cost=65536, parallelism=4)
hash = ph.hash("user_password")
ph.verify(hash, "user_password")  # Returns True or raises exception

# GOOD: Cryptographic random
import secrets
token = secrets.token_hex(32)
api_key = secrets.token_urlsafe(48)
Lesson 06 · Slide 20

Key Management Lifecycle

Every cryptographic key must follow a defined lifecycle:

  1. Generation — Use cryptographically secure random number generators; appropriate key length
  2. Distribution — Secure transport (TLS, out-of-band); never email or chat keys
  3. Storage — HSMs, KMS, or Vault; never in source code or plain-text config
  4. Usage — Enforce least-privilege access; separate encryption and signing keys
  5. Rotation — Regular schedule (30-90 days); automated via secrets manager
  6. Revocation — Immediate on compromise; CRLs or OCSP for certificates
  7. Destruction — Cryptographic erasure; verify all copies are removed

Rotation policy guidelines

API keys: 90 days. Database passwords: 30 days. TLS certificates: annually (or shorter with ACME). Signing keys: annually with overlap period for verification.

Lesson 06 · Slide 21

Automated Key Rotation

"""Automated key rotation script using Vault + AWS Secrets Manager"""
import boto3
import hvac
import logging
from datetime import datetime, timedelta

logger = logging.getLogger(__name__)

def rotate_database_credentials(vault_client: hvac.Client, secret_path: str):
    """Rotate database credentials via Vault dynamic secrets."""
    # Read current lease
    current = vault_client.secrets.kv.v2.read_secret_version(path=secret_path)
    lease_id = current.get("lease_id")

    # Revoke old credentials
    if lease_id:
        vault_client.sys.revoke_lease(lease_id)
        logger.info(f"Revoked old lease: {lease_id}")

    # Generate new dynamic credentials
    new_creds = vault_client.read("database/creds/app-role")
    logger.info(f"New credentials generated, TTL: {new_creds['lease_duration']}s")
    return new_creds["data"]

def rotate_api_key(sm_client, secret_id: str, api_provider):
    """Rotate an API key: generate new, update store, revoke old."""
    # Step 1: Generate new key with provider
    new_key = api_provider.create_api_key()

    # Step 2: Update secrets manager
    sm_client.put_secret_value(
        SecretId=secret_id,
        SecretString=json.dumps({"api_key": new_key, "rotated_at": str(datetime.utcnow())})
    )

    # Step 3: Revoke old key (after grace period for in-flight requests)
    old_key = api_provider.get_current_key()
    api_provider.schedule_revocation(old_key, delay=timedelta(minutes=5))
    logger.info(f"Rotation complete for {secret_id}")
Lesson 06 · Slide 22

TLS/SSL Configuration: Getting It Right

A misconfigured TLS setup can be worse than no encryption — it provides a false sense of security.

# GOOD: Strong Nginx TLS configuration
server {
    listen 443 ssl http2;
    server_name app.example.com;

    # Certificate and key
    ssl_certificate     /etc/letsencrypt/live/app.example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/app.example.com/privkey.pem;

    # Protocol versions — TLS 1.2 and 1.3 only
    ssl_protocols TLSv1.2 TLSv1.3;

    # Strong cipher suites (TLS 1.2)
    ssl_ciphers 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384';
    ssl_prefer_server_ciphers on;

    # HSTS — force HTTPS for 1 year, include subdomains
    add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;

    # OCSP Stapling
    ssl_stapling on;
    ssl_stapling_verify on;
    ssl_trusted_certificate /etc/letsencrypt/live/app.example.com/chain.pem;

    # DH parameters (2048-bit minimum)
    ssl_dhparam /etc/nginx/dhparam.pem;

    # Session configuration
    ssl_session_timeout 1d;
    ssl_session_cache shared:SSL:10m;
    ssl_session_tickets off;
}
Lesson 06 · Slide 23

TLS Misconfigurations to Detect

MisconfigurationRiskDetection
SSLv3 / TLS 1.0 / 1.1 enabledPOODLE, BEAST attacksnmap --script ssl-enum-ciphers
Weak cipher suitesRC4, DES, NULL cipherstestssl.sh, SSL Labs
Expired certificatesBrowser warnings, MITM riskopenssl s_client
Self-signed in productionNo chain of trustCertificate chain validation
Missing HSTSSSL stripping attacksHTTP response header check
Certificate key too smallRSA < 2048 bitsopenssl x509 -text
Wildcard cert overuseLarger blast radius if compromisedCertificate inventory audit
# Quick TLS audit with testssl.sh
testssl.sh --severity HIGH https://app.example.com

# Check certificate expiry
echo | openssl s_client -connect app.example.com:443 2>/dev/null | \
  openssl x509 -noout -dates -subject -issuer

# Check supported protocols and ciphers
nmap --script ssl-enum-ciphers -p 443 app.example.com
Lesson 06 · Slide 24

Certificate Lifecycle Management

Manual certificate management leads to outages. Automate the entire lifecycle:

# Automated certificate issuance with Let's Encrypt + certbot
certbot certonly --nginx -d app.example.com --non-interactive --agree-tos

# Auto-renewal (crontab)
0 0 1 * * certbot renew --post-hook "systemctl reload nginx"
"""AI-assisted certificate monitoring and alerting."""
import ssl
import socket
from datetime import datetime, timedelta

def check_cert_expiry(hostname: str, port: int = 443) -> dict:
    """Check certificate expiry and return status."""
    context = ssl.create_default_context()
    with socket.create_connection((hostname, port)) as sock:
        with context.wrap_socket(sock, server_hostname=hostname) as ssock:
            cert = ssock.getpeercert()
            expiry = datetime.strptime(cert['notAfter'], '%b %d %H:%M:%S %Y %Z')
            days_left = (expiry - datetime.utcnow()).days
            return {
                "hostname": hostname,
                "expiry_date": expiry.isoformat(),
                "days_remaining": days_left,
                "issuer": dict(x[0] for x in cert['issuer']),
                "status": "CRITICAL" if days_left < 7
                          else "WARNING" if days_left < 30
                          else "OK"
            }

# Monitor all domains
domains = ["app.example.com", "api.example.com", "admin.example.com"]
for domain in domains:
    status = check_cert_expiry(domain)
    if status["status"] != "OK":
        send_alert(f"Certificate for {domain} expires in {status['days_remaining']} days!")
Lesson 06 · Slide 25

AI-Assisted Certificate Automation

Use AI to automate certificate inventory, detect anomalies, and recommend actions:

CERT_AUDIT_PROMPT = """Analyze this certificate inventory and provide recommendations:

{cert_inventory}

For each certificate, evaluate:
1. Is the key size adequate (RSA >= 2048, ECDSA >= 256)?
2. Is the signature algorithm secure (not SHA-1)?
3. Is the certificate close to expiry (< 30 days)?
4. Are there any wildcard certs that should be replaced with specific ones?
5. Is the issuer a trusted CA?
6. Are Subject Alternative Names (SANs) appropriately scoped?

Provide a prioritized remediation plan."""

def ai_cert_audit(cert_data: list[dict]) -> str:
    """Use AI to audit certificate inventory."""
    inventory = "\n".join(
        f"- {c['hostname']}: issuer={c['issuer']}, expires={c['expiry_date']}, "
        f"key={c['key_type']} {c['key_size']}-bit, algo={c['sig_algorithm']}"
        for c in cert_data
    )
    response = client.messages.create(
        model="claude-sonnet-4-20250514",
        max_tokens=2048,
        messages=[{"role": "user", "content": CERT_AUDIT_PROMPT.format(cert_inventory=inventory)}]
    )
    return response.content[0].text
Lesson 06 · Slide 26

AI-Assisted Crypto Audit

Use AI to identify weak cryptographic implementations in your codebase:

CRYPTO_AUDIT_PROMPT = """You are a cryptography security auditor. Analyze this code for:

1. **Weak algorithms**: MD5, SHA-1, DES, RC4, ECB mode
2. **Improper key management**: hardcoded keys, insufficient key length
3. **Nonce/IV issues**: reuse, predictable values, static nonces
4. **Random number generation**: using non-CSPRNG for security purposes
5. **Password storage**: unsalted hashes, fast hashing algorithms
6. **Padding oracle risks**: CBC without HMAC, custom padding
7. **Missing authentication**: encryption without integrity (AES-CBC without MAC)

For each finding, provide:
- Severity: CRITICAL / HIGH / MEDIUM / LOW
- The vulnerable code line
- The specific weakness
- A corrected implementation

Code:
```
{code}
```"""

# Example findings from AI audit:
# CRITICAL: AES-ECB mode at line 42 — patterns preserved in ciphertext
# CRITICAL: MD5 password hash at line 67 — use Argon2id instead
# HIGH: Static IV at line 89 — generate random IV per encryption
# MEDIUM: RSA-1024 key at line 15 — upgrade to RSA-2048 minimum
Lesson 06 · Slide 27

Full Codebase Secrets & Crypto Audit

"""Comprehensive AI-powered security audit pipeline."""
import os
import subprocess
import json

def full_audit(repo_path: str) -> dict:
    results = {"secrets": [], "crypto": [], "tls": []}

    # Phase 1: Automated secret scanning with Gitleaks
    gitleaks_out = subprocess.run(
        ["gitleaks", "detect", "--source", repo_path, "--report-format", "json",
         "--report-path", "/tmp/gitleaks.json"],
        capture_output=True, text=True
    )
    if os.path.exists("/tmp/gitleaks.json"):
        with open("/tmp/gitleaks.json") as f:
            results["secrets"] = json.load(f)

    # Phase 2: AI-powered contextual review of flagged files
    for finding in results["secrets"]:
        file_path = os.path.join(repo_path, finding["File"])
        with open(file_path) as f:
            content = f.read()
        # AI validates and enriches findings
        ai_analysis = ai_secret_scan(content)
        finding["ai_assessment"] = ai_analysis

    # Phase 3: Crypto pattern scan
    crypto_patterns = ["MD5", "SHA1", "DES", "ECB", "RC4",
                       "random.random", "Math.random"]
    for root, dirs, files in os.walk(repo_path):
        for fname in files:
            if fname.endswith((".py", ".js", ".java", ".go")):
                fpath = os.path.join(root, fname)
                with open(fpath) as f:
                    code = f.read()
                if any(p.lower() in code.lower() for p in crypto_patterns):
                    results["crypto"].append({
                        "file": fpath,
                        "ai_audit": ai_crypto_audit(code)
                    })

    return results
Lesson 06 · Slide 28

Proper .env and Configuration Management

What to commit

# .env.example (committed)
DATABASE_URL=
AWS_ACCESS_KEY_ID=
AWS_SECRET_ACCESS_KEY=
STRIPE_SECRET_KEY=
OPENAI_API_KEY=
JWT_SECRET=
# .gitignore (committed)
.env
.env.local
.env.production
*.pem
*.key
credentials.json
service-account.json

What NOT to commit

# .env (NEVER committed)
DATABASE_URL=postgresql://...
AWS_ACCESS_KEY_ID=AKIA...
AWS_SECRET_ACCESS_KEY=wJal...
STRIPE_SECRET_KEY=sk_live_...
OPENAI_API_KEY=sk-proj-...
JWT_SECRET=real-secret-here
# Load at runtime
from dotenv import load_dotenv
import os

load_dotenv()
db_url = os.environ["DATABASE_URL"]
# Fails fast if not set
Lesson 06 · Slide 29

Defense in Depth: Layered Secrets Protection

No single control is sufficient. Layer your defenses:

  1. Prevention — Pre-commit hooks (git-secrets, gitleaks protect)
  2. Detection — CI/CD pipeline scanning (TruffleHog, Gitleaks in CI)
  3. Monitoring — Continuous scanning (GitGuardian, GitHub Secret Scanning)
  4. Centralization — Vault / Secrets Manager for all secret storage
  5. Rotation — Automated rotation with zero-downtime deployment
  6. AI augmentation — Contextual analysis to reduce false positives and catch evasions
  7. Incident response — Automated revocation playbooks when leaks are detected

The 5-minute rule

If a secret is leaked, you have approximately 5 minutes before automated scrapers find and exploit it. Your revocation process must be faster than that.

Lesson 06 · Slide 30

Incident Response: When Secrets Leak

"""Automated incident response for leaked secrets."""

RESPONSE_PLAYBOOK = {
    "aws_key": {
        "revoke": "aws iam delete-access-key --access-key-id {key_id} --user-name {user}",
        "audit": "aws cloudtrail lookup-events --lookup-attributes AttributeKey=AccessKeyId,AttributeValue={key_id}",
        "rotate": "aws iam create-access-key --user-name {user}",
    },
    "github_token": {
        "revoke": "gh api -X DELETE /user/installations/{installation_id}/tokens",
        "audit": "gh api /user/audit-log",
    },
    "stripe_key": {
        "revoke": "# Revoke via Stripe Dashboard > API Keys > Roll Key",
        "audit": "stripe events list --type=charge.succeeded --limit=100",
    },
}

def respond_to_leak(secret_type: str, secret_value: str):
    """Execute incident response playbook."""
    playbook = RESPONSE_PLAYBOOK.get(secret_type)
    if not playbook:
        alert_security_team(secret_type, secret_value)
        return

    # Step 1: IMMEDIATELY revoke the compromised credential
    execute(playbook["revoke"])

    # Step 2: Audit for unauthorized usage
    audit_results = execute(playbook["audit"])

    # Step 3: Generate new credentials and update Vault
    new_creds = execute(playbook["rotate"])
    update_vault(secret_type, new_creds)

    # Step 4: Notify security team with full timeline
    notify_team(secret_type, audit_results)
Lesson 06 · Slide 31

HSTS: HTTP Strict Transport Security

HSTS tells browsers to only connect via HTTPS, preventing SSL stripping attacks:

# Check if HSTS is set
curl -sI https://app.example.com | grep -i strict-transport

# Proper HSTS header
Strict-Transport-Security: max-age=31536000; includeSubDomains; preload
DirectivePurposeRecommendation
max-ageHow long to enforce HTTPS (seconds)31536000 (1 year) minimum
includeSubDomainsApply to all subdomainsUse if all subdomains support HTTPS
preloadSubmit to browser preload listUse for maximum protection

HSTS Preload warning

Once on the preload list, removal takes months. Ensure all subdomains support HTTPS before enabling includeSubDomains with preload.

Lesson 06 · Slide 32

Strong Cipher Suite Selection

Choose cipher suites that provide forward secrecy and authenticated encryption:

# TLS 1.3 cipher suites (all are strong — no configuration needed)
TLS_AES_256_GCM_SHA384
TLS_CHACHA20_POLY1305_SHA256
TLS_AES_128_GCM_SHA256

# TLS 1.2 recommended cipher suites (order matters)
ECDHE-ECDSA-AES256-GCM-SHA384
ECDHE-RSA-AES256-GCM-SHA384
ECDHE-ECDSA-CHACHA20-POLY1305
ECDHE-RSA-CHACHA20-POLY1305
ECDHE-ECDSA-AES128-GCM-SHA256
ECDHE-RSA-AES128-GCM-SHA256

Required properties

  • ECDHE — Forward secrecy
  • GCM/POLY1305 — AEAD (authenticated encryption)
  • AES-128+ — Strong symmetric cipher

Reject these

  • RC4 — Biased output, broken
  • CBC — Padding oracle risks
  • NULL — No encryption at all
  • EXPORT — Intentionally weak
Lesson 06 · Slide 33

Secure TLS in Python Applications

"""Proper TLS configuration for Python applications."""
import ssl
import certifi

def create_secure_ssl_context() -> ssl.SSLContext:
    """Create a hardened SSL context."""
    ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)

    # Minimum TLS 1.2
    ctx.minimum_version = ssl.TLSVersion.TLSv1_2

    # Load system CA certificates
    ctx.load_default_certs()
    # Or use certifi bundle
    ctx.load_verify_locations(certifi.where())

    # Enable certificate verification
    ctx.check_hostname = True
    ctx.verify_mode = ssl.CERT_REQUIRED

    # Disable compression (CRIME attack)
    ctx.options |= ssl.OP_NO_COMPRESSION

    return ctx

# BAD: Disabling certificate verification
import requests
requests.get("https://api.example.com", verify=False)  # NEVER in production!

# GOOD: Proper certificate handling
session = requests.Session()
session.verify = certifi.where()  # or True for system certs
response = session.get("https://api.example.com")

# GOOD: Mutual TLS (mTLS)
ctx = create_secure_ssl_context()
ctx.load_cert_chain(certfile="client.pem", keyfile="client-key.pem")
response = requests.get("https://api.example.com", cert=("client.pem", "client-key.pem"))
Lesson 06 · Slide 34

Secrets Management Architecture

A production-ready secrets architecture integrates multiple components:

                    +---------------------+
                    |   Developer Laptop  |
                    | git-secrets (hook)  |
                    +--------+------------+
                             |
                    +--------v------------+
                    |    CI/CD Pipeline   |
                    | Gitleaks + AI scan  |
                    +--------+------------+
                             |
              +--------------+--------------+
              |                             |
    +---------v----------+      +-----------v---------+
    |   HashiCorp Vault  |      |  AWS Secrets Manager |
    | (dynamic secrets)  |      |  (static secrets)    |
    +---------+----------+      +-----------+----------+
              |                             |
    +---------v-----------------------------v----------+
    |              Application Runtime                  |
    |  - Secrets fetched at startup / on-demand         |
    |  - Short TTLs, auto-renewal                       |
    |  - No secrets in environment variables if possible |
    +--------------------------------------------------+
Notes: Environment variables are better than hardcoded secrets, but Vault sidecar injection or SDK-based retrieval is the gold standard.
Lesson 06 · Slide 35

CTF Challenge: Secrets Scavenger Hunt

Mission: Find All Leaked Secrets & Implement Proper Management

You have been given access to a vulnerable codebase. Your objectives:

  1. Discovery — Use TruffleHog, Gitleaks, and AI scanning to find ALL leaked secrets in the repository (including git history)
  2. Classification — Categorize each secret: type, severity, potential blast radius
  3. Triage — Determine which secrets are still valid/active (use --only-verified)
  4. Remediation — Implement proper secrets management:
    • Move all secrets to HashiCorp Vault or equivalent
    • Update application code to fetch secrets from Vault at runtime
    • Add pre-commit hooks to prevent future leaks
    • Configure CI/CD pipeline scanning
  5. Crypto audit — Find and fix all weak cryptographic implementations
  6. TLS hardening — Fix the Nginx configuration to achieve an A+ SSL Labs rating

Scoring

Points awarded for: secrets found (10 pts each), proper Vault integration (25 pts), pre-commit hooks (15 pts), crypto fixes (20 pts), TLS A+ rating (20 pts), AI-augmented report (10 pts).

Lesson 06 · Slide 36

CTF Challenge: Hints & Toolkit

# Step 1: Clone and scan the challenge repository
git clone https://ctf-challenge.example.com/secrets-hunt.git
cd secrets-hunt

# Step 2: Run automated scanners
trufflehog git file://. --json > trufflehog_results.json
gitleaks detect --source . --report-format json --report-path gitleaks_results.json

# Step 3: Check git history for removed secrets
git log --all --diff-filter=D -- "*.env" "*.pem" "*.key"
git log --all -p -S "password" --source --all

# Step 4: Scan Docker images
docker history --no-trunc challenge-app:latest
docker save challenge-app:latest | tar -xf - -C /tmp/layers/

# Step 5: Check CI/CD configuration
cat .github/workflows/*.yml | grep -i secret
cat .gitlab-ci.yml | grep -i -A2 variables

# Step 6: AI-powered deep scan
python ai_secret_scanner.py --repo . --output report.json

# Step 7: Crypto audit
grep -rn "MD5\|SHA1\|DES\|ECB\|RC4" --include="*.py" --include="*.js" .
Notes: Remind students that git history contains deleted secrets. A file removed from HEAD is NOT removed from the repository. Use git filter-branch or BFG Repo-Cleaner for true removal.
Lesson 06 · Slide 37

Key Takeaways

Next lesson

Lesson 07 will cover network traffic analysis and intrusion detection — using AI to identify malicious patterns in real-time network data.

1 / ?