Files
2026-06-23 13:39:43 +07:00

4.8 KiB

Examples: secrets_and_phi_safety

Correct Examples

Secret Loading

# Good: Loading from environment variables
import os

API_KEY = os.environ.get("EXTERNAL_API_KEY")
if not API_KEY:
    raise ValueError("EXTERNAL_API_KEY environment variable not set")

DATABASE_URL = os.environ.get("DATABASE_URL")
SECRET_KEY = os.environ.get("SECRET_KEY", "default-dev-key-change-in-prod")
// Good: Node.js environment variables
const apiKey = process.env.API_KEY;
const dbPassword = process.env.DB_PASSWORD;

if (!apiKey || !dbPassword) {
  throw new Error('Missing required environment variables');
}

PHI Protection in Logs

# Good: Logging without PHI
import logging
import hashlib

logger = logging.getLogger(__name__)

def process_patient_record(patient_record):
    # Log only non-identifying information
    logger.info(
        f"Processing record for patient age group: {get_age_group(patient_record.age)}"
    )
    
    # If logging an ID is absolutely necessary, use a hash
    patient_hash = hashlib.sha256(
        patient_record.medical_record_number.encode()
    ).hexdigest()[:8]
    logger.debug(f"Processing hashed MRN: {patient_hash}...")
    
    # Never do this:
    # logger.info(f"Processing patient {patient_record.name} (MRN: {patient_record.mrn})")
    # logger.error(f"Failed to process {patient_record.id}: {str(e)}")  # if id is PHI

Test Data with Synthetic Identifiers

# Good: Test fixtures use SYNTH-* prefix
TEST_PATIENTS = [
    {
        "patient_id": "SYNTH-PAT-0001",
        "name": "John Doe",  # Note: In real tests, even names should be fake/synthetic
        "mrn": "SYNTH-MRN-0001",
        "date_of_birth": "1980-01-01",
        # ... other test fields
    },
    {
        "patient_id": "SYNTH-PAT-0002",
        "name": "Jane Smith",
        "mrn": "SYNTH-MRN-0002",
        "date_of_birth": "1992-05-15",
    }
]

Configuration Files

# Good: docker-compose.yml using environment variables
version: '3.8'
services:
  api:
    image: medical-api:latest
    environment:
      - DATABASE_URL=${DATABASE_URL}
      - API_KEY=${API_KEY}
      - LOG_LEVEL=${LOG_LEVEL:-info}
    secrets:
      - db_cert  # Reference to external secret store, not file in repo

# Secrets are NOT stored in the repo
# Use Docker secrets or Kubernetes secrets or similar external secret management
# Good: config.ini with environment variable substitution
[database]
url = ${DATABASE_URL}
# or using os.path.expandvars equivalent

.gitignore Protection

# .gitignore at repository root
# ... other entries
secrets/
*.env
*.pem
*.key

Safe Error Messages

# Good: Generic error messages that don't leak PHI
try:
    process_claim(claim_data)
except ValidationError as e:
    # Log detailed error internally (without PHI)
    logger.warning(f"Claim validation failed: {e}")
    # Return generic message to user
    raise HTTPException(
        status_code=400,
        detail="Claim validation failed. Please check the submitted information."
    )
except Exception as e:
    logger.error(f"Unexpected error processing claim: {e}")
    raise HTTPException(
        status_code=500,
        detail="Internal server error. Please contact support."
    )

Incorrect Examples

Secret Exposure

# Bad: Hard-coded secret
API_KEY = "sk_live_1234567890abcdef"  # VIOLATION: exposed in code

# Bad: Secret in config file committed to repo
# config.yaml
# api_key: "actual_secret_key_here"  # VIOLATION

# Bad: Using .env file that gets committed
# .env (committed to git)
# DATABASE_URL=postgres://user:password@localhost/db

PHI in Logs and Errors

# Bad: Logging full patient identifier
logger.error(f"Failed to update patient {patient.patient_id}: {str(e)}")
# If patient_id is MRN or similar, this leaks PHI

# Bad: Error message with PHI
raise ValueError(f"Invalid DOB for patient {patient.name}: {patient.dob}")

# Bad: Including PHI in HTTP response
return {
    "error": f"Patient {patient.mrn} not found",  # MRN is PHI
    "code": "PATIENT_NOT_FOUND"
}

Real Data in Tests

# Bad: Test using real patient-like data
TEST_PATIENT = {
    "patient_id": "12345678",  # Looks like real MRN
    "name": "John Doe",        # Realistic name
    "ssn": "123-45-6789",      # Actual SSN format
    "dob": "1980-01-01",
}

Missing .gitignore Protection

# .gitmissing: secrets/ directory NOT ignored
# This would allow secrets to be committed
# (no entry for secrets/ here)

Insecure Configuration

# Bad: Hard-coded secrets in config
database:
  host: localhost
  port: 5432
  username: admin
  password: "super_secret_password"  # VIOLATION

tls:
  cert_file: "/etc/ssl/certs/real-cert.pem"  # Hard-coded path, should be env var
  key_file: "/etc/ssl/private/real-key.pem"