Skip to content

Enterprise Security: Compliance, Credentials, and Data

Core Concept LearningAugust 18, 202612 min read

Healthcare, finance, and legal teams cannot send data to third-party APIs unless those APIs are certified compliant. Claude Code in regulated environments must guarantee: data is never logged or stored, credentials are rotated regularly, network access is isolated, and audit trails are immutable.

This guide covers enterprise security: zero-data retention (ZDR) setup, HIPAA and regulatory compliance, credential rotation, network isolation, data residency, audit logging, key management, and PII handling. You'll learn to configure Claude Code so it meets SOC 2, HIPAA, PCI-DSS, and GDPR requirements. For the self-hosted deployment model that supports full network isolation, see self-hosted Claude Code deployment; for spend controls that complement access controls, see cost analytics and budget limits.

Zero Data Retention (ZDR) Setup

Zero-data retention means Anthropic API receives requests but doesn't log, store, or use the data for training. This is required for healthcare, legal, and finance where your inputs are protected information.

Enable ZDR via an API parameter: anthropic-beta: disable-message-logging. Anthropic logs nothing about your requests—not inputs, not outputs, not model choice. Your data is used only to generate a response, then discarded.

Configure your gateway to always add this header. Audit: log that every request is sent with ZDR enabled. If a request is missing the header, reject it.

Quick reference

  • ZDR header: 'anthropic-beta: disable-message-logging' sent with every request.
  • API logs: Anthropic does not store your requests/responses if ZDR is enabled.
  • Local logs: Claude Code instances can log (to your system, not Anthropic); configure as needed.
  • Audit: track that ZDR header is present; alert if missing.
  • Verification: periodic audit to confirm ZDR is enabled in production.
ZDR Setup
1# Add ZDR header to every request2 3import anthropic4 5client = anthropic.Anthropic(6    api_key=os.getenv("ANTHROPIC_API_KEY")7)8 9headers = {10    "anthropic-beta": "disable-message-logging"11}12 13response = client.messages.create(14    model="claude-3-5-opus",15    max_tokens=2048,16    messages=[17        {"role": "user", "content": "Analyze this medical record..."}18    ],19    extra_headers=headers20)21 22# Verify ZDR is enabled in production23assert "disable-message-logging" in response.headers.get("anthropic-beta", "")
ZDR Audit
1# Audit: ensure ZDR is enforced2 3def audit_zdr_compliance(days: int = 7):4    events = query_api_events(last_n_days=days)5 6    missing_zdr = [7        e for e in events8        if "disable-message-logging" not in e.get("headers", {}).get("anthropic-beta", "")9    ]10 11    if missing_zdr:12        logger.error(13            f"Found {len(missing_zdr)} requests without ZDR header. "14            "This violates compliance policy."15        )16        alert_security_team(17            f"ZDR violation: {len(missing_zdr)} unprotected requests"18        )19    else:20        logger.info(f"Audit passed: all {len(events)} requests have ZDR enabled")

Remember this

Always send 'anthropic-beta: disable-message-logging' header; audit that it's present on every request; configure gateway to enforce.

HIPAA and Regulatory Compliance

HIPAA (healthcare), SOC 2, PCI-DSS (payments), and GDPR (data privacy) all have overlapping requirements: encryption, audit logs, access control, and incident response.

Claude Code must meet these via:

1. Encryption in transit (TLS 1.3) and at rest (AES-256). 2. Access controls: only authorized users can run code. 3. Audit logs: immutable record of all access and actions. 4. Incident response: process to detect, respond, and report breaches. 5. Regular audits: third-party assessment.

Work with your legal/compliance team. Most will have a Security Questionnaire (SF-635, SOC 2 checklist, HIPAA Risk Assessment). Map Claude Code deployment to each requirement.

Quick reference

  • HIPAA: requires Business Associate Agreement (BAA) with API provider; ZDR must be enabled.
  • SOC 2 Type II: third-party audit of your controls; shows you have strong security posture.
  • PCI-DSS: if handling payment data, encryption and access control required.
  • GDPR: data residency, right to deletion, Data Protection Impact Assessment (DPIA).
  • Questionnaire: compliance team provides detailed checklist; you answer and provide evidence.
HIPAA Checklist
1# HIPAA compliance checklist2 3compliance_checklist = {4    "encryption": {5        "tls_version": "1.3",6        "evidence": "/etc/ssl/certs/tls-1-3-config.txt"7    },8    "authentication": {9        "mfa_required": True,10        "mfa_method": "TOTP or hardware key"11    },12    "audit_logging": {13        "log_retention_days": 2555,  # 7 years14        "log_storage": "encrypted S3 with MFA delete"15    },16    "access_control": {17        "principle": "least privilege",18        "role_based": True,19        "approval_workflow": True20    },21    "incident_response": {22        "response_time_hours": 4,23        "notification_template": "/policies/incident-notification.txt"24    }25}
Evidence Collection
1# Evidence collection for audit2 3def collect_compliance_evidence():4    evidence = {5        "tls_cert": query_tls_certificate_version(),6        "audit_logs": count_audit_log_events(days=90),7        "encryption_config": read_file("/etc/encryption/config.yaml"),8        "access_controls": list_iam_roles_and_permissions(),9        "incident_reports": list_security_incidents(days=365),10    }11 12    # Generate audit report13    report = {14        "assessment_date": datetime.now().isoformat(),15        "controls_status": {16            "tls": "PASS" if evidence["tls_cert"] >= "1.3" else "FAIL",17            "audit_logs": "PASS" if evidence["audit_logs"] > 0 else "FAIL",18            "encryption": "PASS" if evidence["encryption_config"] else "FAIL",19            "access_control": "PASS" if evidence["access_controls"] else "FAIL",20        },21        "evidence_artifacts": evidence22    }23 24    return report

Remember this

Enable ZDR; use TLS 1.3 and AES-256; maintain 7-year audit logs; implement MFA and role-based access; respond to incidents within 4 hours.

Credential Rotation Patterns

API keys, SSH keys, database passwords—all credentials must rotate every 90 days. Rotation ensures: (1) leaked keys expire automatically, (2) no single key is used forever, (3) you practice recovery (important if you need emergency rotation).

Implement a credential rotation schedule: new credential issued 7 days before expiry, both old and new work, after grace period (7 days), old one is deleted. Test rotation on a non-prod system first.

Automate: a scheduled job (GitHub Actions, K8s CronJob) rotates credentials, updates systems, and logs the action.

Quick reference

  • Rotation interval: 90 days for API keys, 30 days for temporary tokens.
  • Grace period: 7 days where old and new both work (overlap period).
  • Testing: dry-run rotation weekly on test environment.
  • Automation: CronJob or GitHub Action triggers rotation; alerts on failure.
  • Rollback: if rotation fails, alert immediately; manual restore from backup.
Key Rotation Automation
1# Credential rotation workflow (GitHub Actions)2 3name: Rotate API Keys4on:5  schedule:6    - cron: "0 0 1 * *"  # 1st of every month7 8jobs:9  rotate:10    runs-on: ubuntu-latest11    steps:12      - uses: actions/checkout@v313      - name: Generate new API key14        env:15          API_ENDPOINT: ${{ secrets.API_ENDPOINT }}16          OLD_KEY: ${{ secrets.ANTHROPIC_API_KEY }}17        run: |18          NEW_KEY=$(curl -X POST $API_ENDPOINT/keys \19            -H "Authorization: Bearer $OLD_KEY" \20            -H "Content-Type: application/json" \21            -d '{"expires_in_days": 90}' | jq -r '.key')22 23          echo "NEW_KEY=$NEW_KEY" >> $GITHUB_ENV24 25      - name: Test new key26        env:27          NEW_KEY: ${{ env.NEW_KEY }}28        run: |29          python test_api_key.py $NEW_KEY30 31      - name: Update secrets32        uses: actions/github-script@v633        with:34          github-token: ${{ secrets.GITHUB_TOKEN }}35          script: |36            await github.rest.actions.createOrUpdateRepoSecret({37              owner: context.repo.owner,38              repo: context.repo.repo,39              secret_name: 'ANTHROPIC_API_KEY',40              encrypted_value: 'NEW_KEY'41            });42 43      - name: Deploy new key to production44        run: |45          kubectl set env deployment/claude-code-gateway \46            ANTHROPIC_API_KEY=${{ env.NEW_KEY }}47 48      - name: Audit log49        run: |50          echo "API key rotated: $(date)" >> rotation-audit.log
Rotation Status
1# Credential rotation status dashboard2 3def get_key_rotation_status():4    keys = query_active_keys()5 6    status = []7    for key in keys:8        days_until_expiry = (key['expires_at'] - datetime.now()).days9        rotation_urgency = "CRITICAL" if days_until_expiry < 7 else "OK"10 11        status.append({12            "key_id": key['id'][:8] + "...",13            "created_at": key['created_at'],14            "expires_at": key['expires_at'],15            "days_until_expiry": days_until_expiry,16            "urgency": rotation_urgency17        })18 19    return status

Remember this

Rotate API keys every 90 days; automate via CI/CD; test rotation on non-prod first; maintain 7-day grace period where old and new both work.

Network Isolation: VPC and Private Links

Regulated data must not cross the public internet. Use VPCs (Virtual Private Cloud) and private links to keep traffic isolated.

For AWS: Claude Code runs in a private subnet (no internet gateway). To reach Anthropic API, use AWS PrivateLink (Anthropic provides an endpoint). All traffic stays on AWS network, never touches the public internet.

For on-prem: isolated network segment, firewall rules restrict traffic, and a bastion host or VPN gateway provides controlled access.

Audit: log all network connections. Alert if data exits your network.

Quick reference

  • VPC: isolate Claude Code runners in private subnet; use NAT gateway if needed for egress.
  • PrivateLink: private endpoint for Anthropic API; no public internet access.
  • Firewall rules: only allow outbound HTTPS to Anthropic API; block everything else.
  • Bastion host: if on-prem, jump host for remote access; SSH only, no RDP.
  • Network logging: VPC Flow Logs capture all network traffic; review for anomalies.
VPC + PrivateLink
1# AWS networking for Claude Code (Terraform)2 3resource "aws_vpc" "claude_code" {4  cidr_block = "10.0.0.0/16"5  enable_dns_hostnames = true6}7 8resource "aws_subnet" "private" {9  vpc_id = aws_vpc.claude_code.id10  cidr_block = "10.0.1.0/24"11  availability_zone = "us-east-1a"12}13 14# PrivateLink endpoint for Anthropic API15resource "aws_vpc_endpoint" "anthropic" {16  vpc_id = aws_vpc.claude_code.id17  service_name = "com.amazonaws.us-east-1.anthropic"  # Example18  vpc_endpoint_type = "Interface"19  subnet_ids = [aws_subnet.private.id]20  security_groups = [aws_security_group.claude_code.id]21}22 23# Security group: only allow HTTPS to Anthropic24resource "aws_security_group" "claude_code" {25  vpc_id = aws_vpc.claude_code.id26 27  egress {28    from_port = 44329    to_port = 44330    protocol = "tcp"31    prefix_lists = [aws_vpc_endpoint.anthropic.prefix_list_id]32  }33 34  egress {35    from_port = 036    to_port = 037    protocol = "-1"38    cidr_blocks = ["0.0.0.0/0"]39    description = "DENY: no other egress allowed"40  }41}
Network Anomaly Detection
1# Network audit: detect anomalous egress2 3def audit_network_egress():4    flows = query_vpc_flow_logs(last_n_days=7)5 6    allowed_destinations = {7        "api.anthropic.com": ["443"]8    }9 10    anomalies = []11    for flow in flows:12        dest = flow['destination_ip']13        port = flow['destination_port']14 15        # Check if destination is in allowlist16        is_allowed = any(17            socket.gethostbyname(hostname) == dest and port in allowed_ports18            for hostname, allowed_ports in allowed_destinations.items()19        )20 21        if not is_allowed:22            anomalies.append({23                "timestamp": flow['timestamp'],24                "source": flow['source_ip'],25                "destination": dest,26                "port": port,27                "severity": "CRITICAL"28            })29 30    if anomalies:31        logger.critical(f"Anomalous network egress detected: {anomalies}")32        alert_security_team(anomalies)33 34    return anomalies

Remember this

Use VPC private subnets and PrivateLink to keep data off the public internet; firewall rules restrict egress; network logs detect anomalies.

Data Residency and Regional Deployment

GDPR, PIPEDA (Canada), and others mandate that personal data stays in-region. If you process EU citizens' data, it must reside in the EU. Canadian data must stay in Canada.

Deploy Claude Code regions that match your data residency rules. Use Anthropic's regional API endpoints (if available) or self-hosted runners in approved regions.

At the gateway, check user's data classification and enforce routing: "This user's data is classified EU-PII; route to eu-west-1 endpoint only."

Quick reference

  • Data classification: tag requests as PII, PHI (health), financial, or public.
  • Residency rules: define which regions can process each classification.
  • Gateway routing: check classification; reject if non-compliant region.
  • Audit: log data flow; verify no misclassified data leaves intended region.
  • Backup: replicate only to approved regions; never cross borders.
Residency Routing
1# Data residency routing2 3def route_request_by_region(user: User, data_classification: str) -> str:4    residency_rules = {5        "eu_pii": ["eu-west-1", "eu-central-1"],6        "ca_pii": ["ca-central-1"],7        "us_phi": ["us-east-1", "us-west-2"],8        "public": ["us-east-1", "eu-west-1", "ap-southeast-1"]9    }10 11    allowed_regions = residency_rules.get(data_classification, [])12 13    if not allowed_regions:14        raise ValueError(f"Unknown data classification: {data_classification}")15 16    # Route to nearest allowed region17    user_region = get_user_region(user)18    if user_region in allowed_regions:19        return user_region20 21    # Default to first allowed region22    return allowed_regions[0]23 24# In API handler25endpoint = route_request_by_region(user, request.data_classification)26response = call_anthropic(endpoint, request)
Residency Audit
1# Audit: verify data residency compliance2 3def audit_data_residency(days: int = 30):4    events = query_api_events(last_n_days=days)5 6    violations = []7    for event in events:8        classification = event['data_classification']9        region = event['region_used']10 11        allowed_regions = residency_rules.get(classification, [])12        if region not in allowed_regions:13            violations.append({14                "timestamp": event['timestamp'],15                "user_id": event['user_id'],16                "classification": classification,17                "region": region,18                "allowed_regions": allowed_regions19            })20 21    if violations:22        logger.error(f"Data residency violations: {violations}")23        report_to_compliance(violations)24 25    return len(violations) == 0

Remember this

Classify data; enforce residency rules at the gateway; route to approved regions only; audit for misclassified data.

Audit Logging and Compliance Events

Audit logs are the source of truth for compliance. They must be: immutable (can't be changed), comprehensive (every access logged), and archived (retained for 7+ years).

Log: who accessed what, when, from where, with what result, and what changed. Store in a write-once system (AWS S3 with Object Lock, or Splunk with immutable indexing).

Compliance teams query these logs during audits. Include: timestamp, user ID, resource, action, outcome, error (if any).

Quick reference

  • Immutability: use S3 Object Lock (WORM) or equivalent; can't be deleted.
  • Retention: 7 years for most regulations; configure archive policies.
  • Content: timestamp, user_id, action, resource, outcome, error_details, ip_address.
  • Real-time alerts: suspicious patterns (rapid repeated failures, after-hours access).
  • Tamper detection: cryptographic signatures; audit logs that check in.
Audit Logging
1# Audit logging2 3import json4from datetime import datetime5 6def log_audit_event(event_type: str, user_id: str, resource: str, action: str, result: str):7    event = {8        "timestamp": datetime.utcnow().isoformat(),9        "event_type": event_type,10        "user_id": user_id,11        "resource": resource,12        "action": action,13        "result": result,14        "ip_address": request.remote_addr,15        "user_agent": request.headers.get("User-Agent", "")16    }17 18    # Write to immutable log store (S3 with Object Lock)19    log_entry = json.dumps(event)20    s3_client.put_object(21        Bucket="audit-logs-immutable",22        Key=f"logs/{datetime.now().year}/{datetime.now().strftime('%m-%d')}/{uuid.uuid4()}.json",23        Body=log_entry,24        ServerSideEncryption="AES256",25        ObjectLockMode="GOVERNANCE",26        ObjectLockRetainUntilDate=datetime.now() + timedelta(days=7*365)27    )28 29    logger.info(log_entry)30 31# Usage32@app.post("/run")33async def run_code(request):34    user_id = request.session["user_id"]35    try:36        result = execute_code(request.code)37        log_audit_event("code_execution", user_id, "code", "execute", "success")38        return result39    except Exception as e:40        log_audit_event("code_execution", user_id, "code", "execute", f"error: {str(e)}")41        raise
Audit Query
1# Audit log query for compliance2 3SELECT4    timestamp,5    event_type,6    user_id,7    resource,8    action,9    result,10    ip_address11FROM audit_logs12WHERE13    timestamp >= DATE_SUB(NOW(), INTERVAL 7 DAY)14    AND (15        result LIKE '%error%'16        OR action IN ('delete', 'modify_permissions')17        OR EXTRACT(HOUR FROM timestamp) NOT BETWEEN 6 AND 2218    )19ORDER BY timestamp DESC

Remember this

Log every access with timestamp, user, action, resource, outcome; store immutably in S3 with Object Lock; retain 7 years; audit suspicious patterns.

Key Management: AWS KMS and Azure Key Vault

Encryption keys must not be stored in code or on disk. Use AWS KMS (Key Management Service) or Azure Key Vault to store and rotate keys. The service manages key lifecycle and provides audit trails.

When Claude Code needs a key (e.g., API key, TLS cert), it requests from KMS/Key Vault. The service decrypts and returns the key. This is more secure than storing keys locally.

For multi-region: replicate keys across regions for failover. Ensure replication is audited.

Quick reference

  • AWS KMS: centralized key management; audit with CloudTrail; encrypt at rest and in transit.
  • Azure Key Vault: similar to KMS; manage certs, keys, and secrets.
  • Encryption: data encrypted with KMS key; only KMS can decrypt (requires IAM permissions).
  • Audit: every decrypt request logged; alert on unusual access patterns.
  • Rotation: automatic or manual; old keys archived; new keys take effect immediately.
KMS Key Retrieval
1# Retrieve API key from AWS KMS2 3import boto34 5kms_client = boto3.client('kms')6 7def get_decrypted_api_key(key_id: str) -> str:8    encrypted_key = os.getenv("ENCRYPTED_ANTHROPIC_API_KEY")9 10    response = kms_client.decrypt(11        CiphertextBlob=base64.b64decode(encrypted_key),12        KeyId=key_id,13        EncryptionContext={14            "service": "claude-code",15            "environment": "production"16        }17    )18 19    return response['Plaintext'].decode('utf-8')20 21# Usage22api_key = get_decrypted_api_key(key_id="arn:aws:kms:us-east-1:123456789:key/...")23client = anthropic.Anthropic(api_key=api_key)
Key Rotation
1# Key rotation with KMS2 3def rotate_kms_key(key_id: str):4    # Enable automatic rotation (annual)5    kms_client.enable_key_rotation(KeyId=key_id)6 7    # Schedule rotation8    response = kms_client.schedule_key_deletion(9        KeyId=key_id,10        PendingWindowInDays=30  # 30-day grace period11    )12 13    logger.info(f"Key rotation scheduled: {response['KeyId']}")14 15    # Audit log16    audit_log({17        "event": "key_rotation",18        "key_id": key_id,19        "scheduled_deletion": response['DeletionDate']20    })

Remember this

Store all secrets in AWS KMS or Azure Key Vault; never hardcode in code or config; rotate automatically; audit every access.

PII Handling and Redaction

Personally Identifiable Information (PII)—names, emails, phone numbers, SSNs—must be redacted before sending to Claude or logging. Implement redaction at the gateway: scan incoming requests for PII patterns, remove or mask before forwarding.

Redaction patterns: Social Security Number (9 digits), phone number (10 digits), email (name@domain), credit card (16 digits).

For GDPR/CCPA: if a user requests data deletion, purge all logs and responses mentioning them.

Quick reference

  • Redaction: scan request for PII; replace with [SSN], [PHONE], [EMAIL], [CC].
  • Logging: log the fact that PII was redacted, but not the PII itself.
  • Deletion: if user requests erasure, delete all traces (logs, caches, backups after retention).
  • False positives: redaction can be over-eager (e.g., order number looks like CC); tune thresholds.
  • Unmasking: auditors may need to see unredacted data; use a separate, more restricted log.
PII Redaction
1# PII redaction at gateway2 3import re4 5PII_PATTERNS = {6    "SSN": r"\b\d{3}-\d{2}-\d{4}\b",7    "PHONE": r"\b\d{3}[-.]?\d{3}[-.]?\d{4}\b",8    "EMAIL": r"\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b",9    "CREDIT_CARD": r"\b\d{4}[\s-]?\d{4}[\s-]?\d{4}[\s-]?\d{4}\b"10}11 12def redact_pii(text: str) -> Tuple[str, dict]:13    redacted = text14    found_pii = {}15 16    for pii_type, pattern in PII_PATTERNS.items():17        matches = re.findall(pattern, text)18        if matches:19            found_pii[pii_type] = len(matches)20            redacted = re.sub(pattern, f"[{pii_type}]", redacted)21 22    return redacted, found_pii23 24# In route handler25@app.post("/v1/messages")26async def proxy_message(request):27    # Redact PII before sending to Claude28    redacted_content, pii_found = redact_pii(request.messages[0]["content"])29 30    if pii_found:31        logger.warning(f"PII detected and redacted: {pii_found}")32        request.messages[0]["content"] = redacted_content33 34    # Send redacted request35    response = call_anthropic(request)36    return response
Right to Deletion
1# GDPR right to deletion (erasure)2 3def delete_user_data(user_id: str):4    # Delete audit logs5    delete_audit_logs_for_user(user_id)6 7    # Delete API response cache8    delete_response_cache(user_id)9 10    # Delete database records11    delete_user_sessions(user_id)12    delete_user_preferences(user_id)13 14    # Request deletion from backup/archive15    archive_retention_days = 2555  # 7 years16    schedule_archive_deletion(user_id, days=archive_retention_days)17 18    # Log the deletion19    log_audit_event(20        "user_data_deletion",21        user_id,22        "user_data",23        "delete",24        "success"25    )26 27    logger.info(f"User {user_id} data deletion complete")

Remember this

Redact PII patterns (SSN, email, phone, CC) before sending to Claude; log redaction fact, not the PII; implement right-to-deletion for GDPR.

Key takeaway

Enterprise security in Claude Code means: zero-data retention to protect privacy, compliance controls for regulated industries, credential rotation to limit exposure, network isolation to prevent data exfiltration, audit logging for forensics, and key management for secrets.

Start with ZDR enabled by default. Add HIPAA/SOC 2 controls if your industry requires. Layer in network isolation and audit logging. As you scale, automate credential rotation and implement key management.

Security isn't a feature; it's a requirement. Build it in from the start.

Next: combine security with cost management to ensure you're tracking not only spend, but also compliance and risk.

Share:
PK

Polo Khan

Lead Author & Systems Architect

Software engineer and distributed systems architect specializing in backend scalability, cloud-native infrastructure, databases, and AI engineering workflows. Author and maintainer of Core Concept Learning.

Human-Engineered & Fact-CheckedOriginal Visual DiagramsEditorial Standards →Send Feedback

Related Articles

Adopting autonomous AI development tools in enterprise engineering organizations requires strict compliance with data pr

Read

When Claude Code traffic grows from 10 to 1000 concurrent users, a single endpoint breaks. You need a gateway: a single

Read

Deploying Large Language Models in healthcare, finance, defense, and legal industries requires strict data privacy contr

Read

Keep learning

Follow a structured path or browse all courses to go deeper.