The handoff problem
SaaS vendors that handle PHI such as telehealth platforms, EHR integrations, diagnostic tools tell customers: "Store your data in your own S3 bucket for HIPAA compliance."
This is architecturally sound. The vendor avoids storing PHI. The customer controls their own data. But it creates a responsibility handoff: the vendor provides instructions, the customer must execute them correctly.
Whereby, a video conferencing platform used in telehealth, tells HIPAA customers to store cloud recordings in their own S3 bucket with this policy:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AllowListAndPutRecordings",
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::<aws_account_id>:user/<aws_user>"
},
"Action": ["s3:ListBucket", "s3:PutObject"],
"Resource": [
"arn:aws:s3:::<recording_bucket>",
"arn:aws:s3:::<recording_bucket>/*"
]
}
]
}
Good template. Scoped principal, minimal actions, specific resources. But the template only covers the bucket policy. It says nothing about encryption, logging, versioning, Public Access Block, Object Lock, or transport encryption.
The customer is responsible for all of it. They can get it wrong.
What goes wrong
1. No encryption at rest
The customer creates the bucket, pastes the policy, starts recording. Nobody enables server-side encryption. The recordings which contain patient video, audio, and potentially screen-shared medical records sit unencrypted in S3.
Stave catches it:
[FAIL] CONTROLS.001 — high
Compliance: §164.312(a)(2)(iv) — Encryption and Decryption
Finding: Bucket telehealth-recordings: encryption at rest not enabled
2. AWS-managed key instead of CMK
The customer enables SSE-S3 (AES256) which is the default. But AES256 uses an AWS-managed key that the customer cannot revoke. During a breach, the customer needs to disable the key immediately to render recordings unreadable. With the AWS-managed key, they can't.
Stave catches it:
[FAIL] CONTROLS.001.STRICT — critical
Compliance: §164.312(a)(2)(iv) — CMK required for key revocation
Finding: SSE uses AES256 (AWS-managed). CMK required for breach response.
Whereby even notes that "key rotation will provide additional security." But they stop short of requiring CMK. Stave's HIPAA profile makes the distinction explicit.
3. No access logging
The bucket stores PHI recordings but has no server access logging enabled. If an unauthorized party downloads recordings, there's no forensic evidence.
Stave catches it:
[FAIL] AUDIT.001 — critical
Compliance: §164.312(b) — Audit Controls
Finding: Bucket telehealth-recordings: logging not enabled.
Logs cannot be obtained retroactively from AWS.
This is the retroactivity problem. You can't turn on logging after a breach and recover what happened before. The evidence window is zero.
4. Public Access Block not enabled
The customer pastes the bucket policy but doesn't enable Public Access Block. A future policy change such as a Terraform apply with a stale config, a console click could make the bucket public. PAB is the safety net.
Stave catches it:
[FAIL] CTL.S3.CONTROLS.001 — high
Finding: Public Access Block not fully enabled. No safety net against
accidental public exposure from policy or ACL changes.
5. Wildcard actions in the policy
Instead of the vendor's scoped template, the customer pastes a broader policy they found on Stack Overflow:
{
"Effect": "Allow",
"Principal": "*",
"Action": "s3:*",
"Resource": "arn:aws:s3:::telehealth-recordings/*"
}
Now anyone can read, write, delete, or modify ACLs on the recordings bucket.
Stave catches it:
[FAIL] ACCESS.002 — high
Compliance: §164.312(a)(2)(i) — Least privilege
Finding: Policy grants Allow with wildcard action s3:*
And the compound detector fires:
[critical] COMPOUND.001
Public access with overly broad IAM permissions — the S3 + IAM lateral
movement pattern present in the majority of documented AWS breaches.
6. No Object Lock for retention
HIPAA requires 6 years of PHI retention. Without Object Lock in Compliance mode, recordings can be deleted accidentally, maliciously, or by a lifecycle rule that expires objects too early.
Stave catches it:
[FAIL] RETENTION.002 — critical
Compliance: §164.316(b)(2) — 6-year PHI retention
Finding: Object Lock not enabled — recordings can be deleted.
7. No transport encryption enforcement
The bucket policy doesn't deny non-TLS requests. Someone accessing the bucket via HTTP (not HTTPS) transmits recordings in plaintext.
Stave catches it:
[FAIL] CONTROLS.004 — high
Compliance: §164.312(e)(2)(ii) — Encryption in transit
Finding: No Deny statement for aws:SecureTransport=false
The compound picture
Run all 14 HIPAA controls against a typical "I followed the vendor's template" bucket:
stave evaluate --snapshot recordings-bucket.json --profile hipaa
═══ HIPAA Security Rule ═══
Bucket: telehealth-recordings
Result: FAIL
critical: 0/4 passed
high: 1/7 passed (only the vendor's bucket policy is correct)
medium: 0/2 passed
Overall: FAIL
The vendor's bucket policy template is the only thing that passes. Everything else such as encryption, logging, retention, transport security, PAB is the customer's responsibility. And the customer didn't do it.
The duration dimension
The worst version of this: the bucket has been in this state since the customer set it up 18 months ago. Every recording since then is stored unencrypted, unlogged, without retention protection.
Duration: 13,000+ hours (threshold: 168 hours)
No other tool tracks this. AWS Config says "non-compliant now." Stave says "non-compliant for 13,000 hours." That temporal evidence changes the regulatory conversation.
The lesson for SaaS vendors and customers
For vendors: providing a bucket policy template is necessary but insufficient. Document the full bucket security posture: encryption (CMK, not AWS-managed), logging, PAB, versioning, Object Lock, transport encryption. Or better: provide a Stave observation contract so customers can evaluate their bucket against your HIPAA requirements.
For customers: the vendor's HIPAA compliance ends at their boundary. Your S3 bucket is your responsibility. Run Stave's HIPAA profile against every bucket that stores vendor-delegated PHI:
stave evaluate --snapshot my-recordings-bucket.json --profile hipaa
If it doesn't say PASS across all 14 controls, the bucket isn't HIPAA compliant regardless of what the vendor's documentation says.
Top comments (0)