Audit Logging & Monitoring
Overviewโ
HIPAA Audit Controls standard (45 CFR ยง 164.312(b)) requires mechanisms that record and examine activity in systems that contain ePHI. Audit logs must be retained for a minimum of 6 years.
1. Cloud Audit Logsโ
| Log Type | What It Captures | Notes |
|---|---|---|
| Admin Activity | API calls that modify resources | Always enabled |
| Data Access | API calls that read data or metadata | Must be explicitly enabled |
| System Event | Automated GCP system actions | Always enabled |
| Policy Denied | Requests denied by VPC-SC or IAM | Enabled by VPC-SC |
Enable Data Access Logsโ
gcloud projects get-iam-policy YOUR_PHI_PROJECT_ID --format=json > iam_policy.json
Edit iam_policy.json to add:
{
"auditConfigs": [
{
"service": "allServices",
"auditLogConfigs": [
{ "logType": "ADMIN_READ" },
{ "logType": "DATA_READ" },
{ "logType": "DATA_WRITE" }
]
},
{
"service": "cloudsql.googleapis.com",
"auditLogConfigs": [
{ "logType": "DATA_READ" },
{ "logType": "DATA_WRITE" }
]
},
{
"service": "secretmanager.googleapis.com",
"auditLogConfigs": [
{ "logType": "DATA_READ" },
{ "logType": "DATA_WRITE" }
]
}
]
}
gcloud projects set-iam-policy YOUR_PHI_PROJECT_ID iam_policy.json
2. 6-Year Log Retentionโ
# Create an immutable log bucket
gcloud storage buckets create gs://your-org-phi-audit-logs \
--location=us-central1 \
--uniform-bucket-level-access \
--public-access-prevention \
--project=YOUR_PHI_PROJECT_ID
# Set 6-year retention policy
gcloud storage buckets update gs://your-org-phi-audit-logs \
--retention-period=2190d --project=YOUR_PHI_PROJECT_ID
# LOCK the retention policy (irreversible โ confirm with compliance team)
gcloud storage buckets update gs://your-org-phi-audit-logs \
--lock-retention-policy --project=YOUR_PHI_PROJECT_ID
# Create a log sink
gcloud logging sinks create phi-audit-sink \
"storage.googleapis.com/your-org-phi-audit-logs" \
--log-filter='protoPayload.@type="type.googleapis.com/google.cloud.audit.AuditLog"' \
--project=YOUR_PHI_PROJECT_ID
# Grant the sink write access
SINK_SA=$(gcloud logging sinks describe phi-audit-sink \
--project=YOUR_PHI_PROJECT_ID --format="value(writerIdentity)")
gcloud storage buckets add-iam-policy-binding gs://your-org-phi-audit-logs \
--member="$SINK_SA" --role="roles/storage.objectCreator"
3. Application-Level Audit Loggingโ
from enum import Enum
from google.cloud import logging as gcp_logging
class AuditAction(str, Enum):
LOGIN = "LOGIN"
LOGOUT = "LOGOUT"
PHI_VIEW = "PHI_VIEW"
PHI_CREATE = "PHI_CREATE"
PHI_UPDATE = "PHI_UPDATE"
PHI_DELETE = "PHI_DELETE"
PHI_EXPORT = "PHI_EXPORT"
ACCESS_DENIED = "ACCESS_DENIED"
class PHIAuditLogger:
"""Logs to Cloud Logging AND database. Never logs PHI values."""
def __init__(self, project_id: str, db_connection):
gcp_client = gcp_logging.Client(project=project_id)
self.logger = gcp_client.logger("phi-audit-log")
self.db = db_connection
def log(self, action: AuditAction, user_id: str, resource_type: str,
resource_id: str, ip_address: str, outcome: str = "SUCCESS"):
phi_keys = {"ssn", "name", "dob", "diagnosis", "address", "phone"}
entry = {
"action": action.value, "user_id": user_id,
"resource_type": resource_type, "resource_id": resource_id,
"ip_address": ip_address, "outcome": outcome,
}
self.logger.log_struct(entry, severity="NOTICE",
labels={"hipaa_audit": "true"})
self.db.execute(
"INSERT INTO audit_log (user_id, action, resource_type, resource_id, "
"ip_address, outcome) VALUES (%s, %s, %s, %s, %s, %s)",
(user_id, action.value, resource_type, resource_id, ip_address, outcome)
)
Events to Logโ
| Event | Required Fields |
|---|---|
| User login | user_id, timestamp, ip, success/failure |
| PHI record viewed | user_id, resource_type, resource_id (no PHI), timestamp |
| PHI record created/updated | user_id, resource_type, resource_id, timestamp |
| PHI export | user_id, format, record_count, timestamp |
| Failed access | user_id or IP, resource, reason, timestamp |
4. Security Alertsโ
| Alert | Threshold | Priority |
|---|---|---|
| Failed logins | > 5 per 5 minutes | HIGH |
| IAM policy changes | Any change | HIGH |
| Firewall rule changes | Any change | HIGH |
| VPC-SC policy violation | Any violation | CRITICAL |
| KMS key deletion requested | Any request | CRITICAL |
| Secret accessed outside business hours | 10pmโ6am | MEDIUM |
# Create log-based metric for failed logins
gcloud logging metrics create failed-logins \
--description="Failed login attempts" \
--log-filter='jsonPayload.action="LOGIN" AND jsonPayload.outcome="FAILURE"' \
--project=YOUR_PHI_PROJECT_ID
5. Useful Log Queriesโ
# All Cloud SQL data access events
gcloud logging read 'resource.type="cloudsql_database" AND logName:"data_access"' \
--project=YOUR_PHI_PROJECT_ID --limit=100
# IAM policy changes
gcloud logging read 'protoPayload.methodName:"SetIamPolicy"' \
--project=YOUR_PHI_PROJECT_ID --limit=50
# Secret Manager access events
gcloud logging read \
'protoPayload.serviceName="secretmanager.googleapis.com"' \
--project=YOUR_PHI_PROJECT_ID --limit=50
Next: Incident Response โ