HIPAA on AWS: Rules, Business Associate Agreements & Covered Services
Understand HIPAA compliance on AWS ā what PHI is, which AWS services are HIPAA-eligible, how to sign a BAA, and the technical safeguards every healthcare application must implement.
What is HIPAA?
HIPAA (Health Insurance Portability and Accountability Act, 1996) is US federal law governing how Protected Health Information (PHI) is used, stored, and transmitted. Non-compliance penalties range from $100 to $1.9M per violation category per year.
Who must comply: | Entity | Examples | |--------|---------| | Covered Entities | Hospitals, clinics, health insurance companies, pharmacies | | Business Associates (BA) | Cloud providers, software vendors, billing services ā anyone who handles PHI on behalf of a covered entity | | Subcontractors | Any vendor a BA uses who also touches PHI |
As a software developer building healthcare apps, your company is almost certainly a Business Associate.
What is PHI?
Protected Health Information is individually identifiable health information ā any data that can link a person to their health condition or medical care.
The 18 HIPAA Identifiers
Any of these, when combined with health information, creates PHI:
- Names
- Geographic data (anything smaller than a state ā addresses, ZIP codes, county)
- Dates (birth dates, admission dates, discharge dates, death dates)
- Phone numbers
- Fax numbers
- Email addresses
- Social Security Numbers
- Medical record numbers
- Health plan beneficiary numbers
- Account numbers
- Certificate/license numbers
- Vehicle identifiers and serial numbers (VINs)
- Device identifiers and serial numbers
- Web URLs
- IP addresses
- Biometric identifiers (fingerprints, voice prints)
- Full-face photographs and comparable images
- Any other unique identifying number or code
Rule of thumb: If you can figure out who the patient is from the data, it's PHI. Encrypt it, audit access to it, and delete it on schedule.
The HIPAA Security Rule: Technical Safeguards
The Security Rule specifies technical controls for electronic PHI (ePHI):
| Safeguard | Requirement | |-----------|------------| | Access Controls | Assign unique user IDs, automatic logoff, encryption/decryption | | Audit Controls | Record and examine access and activity | | Integrity | Protect ePHI from improper alteration or destruction | | Transmission Security | Encrypt ePHI in transit (TLS 1.2+) |
AWS and the Business Associate Agreement (BAA)
AWS is a Business Associate. Before you can store or process PHI on AWS, you must sign a BAA with Amazon.
Signing the AWS BAA
- Go to AWS Artifact in the AWS Console
- Navigate to Agreements ā AWS Business Associate Addendum
- Accept the agreement
The BAA covers your entire AWS account. You sign it once ā it applies to all covered services in that account.
Critical: The BAA only covers HIPAA-eligible AWS services. Using non-covered services for PHI violates the BAA. AWS publishes the full list at aws.amazon.com/compliance/hipaa-eligible-services-reference.
HIPAA-Eligible AWS Services (Key Ones)
| Service | Use Case | |---------|---------| | Amazon EC2 | Application servers, managed EC2 workloads | | Amazon RDS | PostgreSQL, MySQL, SQL Server databases | | Amazon Aurora | High-performance managed SQL | | Amazon DynamoDB | NoSQL, key-value and document storage | | Amazon S3 | File storage (medical images, documents, exports) | | Amazon S3 Glacier | Long-term archival | | Amazon ECS / EKS | Container orchestration | | AWS Lambda | Serverless compute | | Amazon API Gateway | HTTP API endpoints | | Amazon SQS | Message queuing | | Amazon SNS | Notifications and pub/sub | | Amazon SES | Sending email (e.g., appointment confirmations) | | AWS KMS | Key management for encryption | | AWS Secrets Manager | Secret storage | | AWS CloudTrail | API audit logging | | Amazon CloudWatch | Logs, metrics, alarms | | AWS WAF | Web application firewall | | Amazon Cognito | User authentication | | Amazon Comprehend Medical | Clinical NLP | | Amazon HealthLake | FHIR-compliant data store |
Services NOT Covered by the BAA
- Amazon CloudFront (at time of writing ā check current list)
- AWS AppSync (check current list)
- Free-tier services generally
Always check the official AWS list before using a service for PHI. The list is updated as AWS adds HIPAA coverage.
Architectural Boundaries: What Counts as PHI in Your System
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
ā PHI Boundary ā
ā ā
ā Patient name + DOB + diagnosis ā PHI ā
ā Patient name + appointment date ā PHI ā
ā Appointment ID alone ā NOT PHI (no individual link) ā
ā IP address + health forum post ā PHI ā
ā De-identified stats ā NOT PHI (if Safe Harbor method applied) ā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāCore Technical Requirements
1. Encryption at Rest
All ePHI at rest must be encrypted. AWS provides this through KMS:
# Terraform: RDS with KMS encryption
resource "aws_kms_key" "rds" {
description = "RDS PHI encryption key"
deletion_window_in_days = 30
enable_key_rotation = true # Annual rotation
tags = { Purpose = "phi-encryption" }
}
resource "aws_db_instance" "postgres" {
identifier = "hipaa-prod-db"
engine = "postgres"
engine_version = "16"
instance_class = "db.t3.medium"
allocated_storage = 100
storage_encrypted = true # Encryption at rest
kms_key_id = aws_kms_key.rds.arn
backup_retention_period = 35 # Minimum 7 days; 35 for compliance
deletion_protection = true
skip_final_snapshot = false
final_snapshot_identifier = "hipaa-prod-final-snapshot"
# Enhanced monitoring for audit evidence
monitoring_interval = 60
monitoring_role_arn = aws_iam_role.rds_monitoring.arn
}
# S3 for medical documents
resource "aws_s3_bucket_server_side_encryption_configuration" "phi" {
bucket = aws_s3_bucket.phi_documents.id
rule {
apply_server_side_encryption_by_default {
sse_algorithm = "aws:kms"
kms_master_key_id = aws_kms_key.s3.arn
}
bucket_key_enabled = true
}
}2. Encryption in Transit
All PHI transmission must be over TLS 1.2 or higher:
# Enforce TLS on S3 bucket
resource "aws_s3_bucket_policy" "enforce_tls" {
bucket = aws_s3_bucket.phi_documents.id
policy = jsonencode({
Version = "2012-10-17"
Statement = [{
Sid = "DenyNonTLS"
Effect = "Deny"
Principal = "*"
Action = "s3:*"
Resource = ["${aws_s3_bucket.phi_documents.arn}/*"]
Condition = {
Bool = { "aws:SecureTransport" = "false" }
}
}]
})
}
# API Gateway: only TLS 1.2+
resource "aws_api_gateway_domain_name" "api" {
domain_name = "api.healthcare.example.com"
regional_certificate_arn = aws_acm_certificate.api.arn
security_policy = "TLS_1_2"
}3. Access Controls: Minimum Necessary
Only allow access to PHI that is required to perform a specific function:
# IAM policy: Lambda can only access specific RDS and S3 paths
data "aws_iam_policy_document" "phi_access" {
# Only read patient records ā no admin operations
statement {
actions = [
"rds-data:ExecuteStatement",
"rds-data:BatchExecuteStatement"
]
resources = [aws_rds_cluster.postgres.arn]
}
# Only access PHI documents bucket with specific prefix
statement {
actions = ["s3:GetObject", "s3:PutObject"]
resources = ["${aws_s3_bucket.phi_documents.arn}/patients/*"]
}
# KMS decrypt/encrypt for the specific key only
statement {
actions = ["kms:Decrypt", "kms:GenerateDataKey"]
resources = [aws_kms_key.phi.arn]
}
}4. Audit Logging with CloudTrail
HIPAA requires audit logs of all PHI access:
# CloudTrail: log all S3 PHI access
resource "aws_cloudtrail" "phi_access" {
name = "phi-access-trail"
s3_bucket_name = aws_s3_bucket.cloudtrail_logs.id
include_global_service_events = true
is_multi_region_trail = true
enable_log_file_validation = true # Detect tampered logs
kms_key_id = aws_kms_key.cloudtrail.arn
event_selector {
read_write_type = "All"
include_management_events = true
data_resource {
type = "AWS::S3::Object"
values = ["${aws_s3_bucket.phi_documents.arn}/"]
}
}
# DynamoDB PHI access
advanced_event_selector {
name = "DynamoDB PHI Data Events"
field_selector {
field = "eventCategory"
equals = ["Data"]
}
field_selector {
field = "resources.ARN"
starts_with = [aws_dynamodb_table.patients.arn]
}
}
}5. Automatic Logoff and Session Management
// .NET: HIPAA-compliant session timeout (15 minutes max)
builder.Services.ConfigureApplicationCookie(options =>
{
options.ExpireTimeSpan = TimeSpan.FromMinutes(15);
options.SlidingExpiration = false; // No extensions ā hard timeout
options.Cookie.SecurePolicy = CookieSecurePolicy.Always;
options.Cookie.HttpOnly = true;
options.Cookie.SameSite = SameSiteMode.Strict;
});Network Architecture: Defense in Depth
Internet
ā
ā¼
AWS WAF (block common attacks)
ā
ā¼
Application Load Balancer (TLS termination, access logs)
ā
ā¼
Private Subnet: Application Servers (ECS/Lambda)
ā
ā¼
Private Subnet: RDS PostgreSQL (no public access)
ā
ā¼
Private Subnet: ElastiCache Redis (sessions, cache)
All traffic: VPC Security Groups + NACLs
No public IP on any database
NAT Gateway for outbound only# VPC with no public subnets for PHI resources
resource "aws_vpc" "hipaa" {
cidr_block = "10.0.0.0/16"
enable_dns_hostnames = true
enable_dns_support = true
tags = { Name = "hipaa-vpc", HIPAA = "true" }
}
# RDS subnet group ā private subnets only
resource "aws_db_subnet_group" "private" {
name = "hipaa-db-private"
subnet_ids = aws_subnet.private[*].id
}
# Security group: RDS only accepts traffic from app layer
resource "aws_security_group" "rds" {
name = "hipaa-rds-sg"
vpc_id = aws_vpc.hipaa.id
ingress {
from_port = 5432
to_port = 5432
protocol = "tcp"
security_groups = [aws_security_group.app.id] # Only from app SG
}
egress {
from_port = 0; to_port = 0; protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
}Monitoring and Breach Detection
# CloudWatch alarm: unusual access patterns
resource "aws_cloudwatch_metric_alarm" "phi_access_spike" {
alarm_name = "phi-unusual-access-volume"
comparison_operator = "GreaterThanThreshold"
evaluation_periods = 1
metric_name = "NumberOfObjects"
namespace = "AWS/S3"
period = 300
statistic = "Sum"
threshold = 1000 # Alert if >1000 S3 objects accessed in 5min
alarm_description = "Unusual PHI access volume ā potential breach"
alarm_actions = [aws_sns_topic.security_alerts.arn]
}
# GuardDuty for threat detection (recommended for HIPAA)
resource "aws_guardduty_detector" "main" {
enable = true
datasources {
s3_logs { enable = true }
malware_protection {
scan_ec2_instance_with_findings {
ebs_volumes { enable = true }
}
}
}
}HIPAA Compliance Checklist
Technical Safeguards
- [ ] All ePHI encrypted at rest (KMS keys with annual rotation)
- [ ] All ePHI encrypted in transit (TLS 1.2 minimum)
- [ ] Unique user IDs ā no shared accounts
- [ ] 15-minute automatic session timeout
- [ ] Audit logs of all PHI access (CloudTrail, application logs)
- [ ] Audit logs retained 6 years, tamper-evident
- [ ] Emergency access procedure documented
- [ ] Minimum necessary access enforced (IAM least privilege)
Operational Safeguards
- [ ] BAA signed with AWS
- [ ] BAA signed with all other vendors who touch PHI
- [ ] Workforce training records maintained
- [ ] Access termination procedure documented
- [ ] Incident response plan (60-day breach notification window)
- [ ] Disaster recovery tested annually
- [ ] Risk assessment conducted and documented
AWS Specific
- [ ] Only HIPAA-eligible AWS services used for PHI
- [ ] CloudTrail enabled in all regions
- [ ] AWS Config enabled for compliance monitoring
- [ ] GuardDuty enabled for threat detection
- [ ] VPC flow logs enabled
- [ ] No PHI in CloudFront logs, Route 53 logs (check current BAA coverage)
- [ ] MFA enabled for all AWS console access
Common Mistakes
Mistake 1: Storing PHI in CloudWatch Logs without thinking Application logs often contain PHI (patient IDs, names in error messages). Sanitise logs before they're emitted, or encrypt the CloudWatch log group with KMS.
Mistake 2: PHI in URL query strings Query strings appear in CloudFront/ALB access logs.
GET /patients?name=alice&dob=1990-01-01is a PHI leak. Use POST body or path params where possible.
Mistake 3: Assuming AWS is responsible for compliance AWS provides compliant infrastructure. Your application must implement access controls, audit logging, and encryption. Shared responsibility model ā AWS secures the cloud, you secure what's in it.
Mistake 4: Not testing PHI deletion HIPAA requires a minimum retention of 6 years. But when the retention period ends, you must be able to prove data was deleted. Test your deletion workflows.
Summary
| Requirement | AWS Solution | |-------------|-------------| | Encryption at rest | KMS + RDS encryption, S3 SSE-KMS | | Encryption in transit | ACM certificates, TLS policy on API GW | | Audit logging | CloudTrail data events + CloudWatch | | Access control | IAM least privilege, Security Groups | | Breach detection | GuardDuty, CloudWatch anomaly alarms | | Compliance evidence | AWS Config, CloudTrail log integrity | | BAA | AWS Artifact ā Business Associate Addendum |
Next up: PHI Encryption ā At-Rest and In-Transit on AWS with column-level encryption.
Enjoyed this article?
Explore the Security & Compliance learning path for more.
Found this helpful?
Leave a comment
Have a question, correction, or just found this helpful? Leave a note below.