Skip to main content
cd ../config-traps
risk/register/s3-bucket-policies-the-missing-sourceaccount-check.html
AWS S3 Bucket Policieshigh severityAWS S3

S3 Bucket Policies: The Missing SourceAccount Check

Severity
high
Reviewed
12 Jul 2026
Remediation
~20 minutes
Overview

A bucket policy grants a wildcard AWS service Principal like sns.amazonaws.com or cloudtrail.amazonaws.com write access without pinning aws:SourceAccount, so any AWS account can configure that same service to target the bucket. IAM boundaries never trigger, because the request arrives as a trusted first-party service call, not a cross-account API invocation.

Operational summary

At a glance

Symptom
A different AWS account configures the same trusted service to target the exposed bucket ARN, and the service delivers on their behalf without any…
Likely cause
An S3 bucket policy grants an AWS service Principal (e.g.
Impact
Unauthenticated writes accumulate as legitimate service traffic, evading GuardDuty and IAM Access Analyzer while inflating storage costs and poisoning downstream log analysis.
Verification signal
Re-run the detection test and a controlled negative-path test.
Safe correction
Require aws:SourceAccount and aws:SourceArn conditions on every service-principal statement and enforce this with a custom AWS Config rule.
Rollback or recovery
Restore the exported configuration if the new control blocks required production traffic, then narrow the policy before redeployment.

The Trap

Service Principal grants in an S3 bucket policy that omit the aws:SourceAccount condition key, trusting an AWS service by name alone rather than scoping which account’s instance of that service may act.

The Default State

Console wizards for S3 event notifications, CloudTrail delivery, ALB access logging, AWS Config recorder delivery, and SES receipt rule storage generate a Principal block such as {“Service”: “cloudtrail.amazonaws.com”} and often include aws:SourceArn, but a large share of hand-written Terraform modules and older CloudFormation samples predating the 2021 confused-deputy advisory skip aws:SourceAccount entirely. Engineers copying a working policy from one project to another rarely notice the omission, since the bucket still functions correctly for the intended account.

The Blast Radius

The policy trusts the named AWS service, not your account specifically. Any AWS customer can create their own CloudTrail trail, SNS topic, or ALB and configure it to deliver output to your bucket ARN if it becomes known through shared logs, Terraform state leaks, or predictable naming. The service then writes objects on their behalf, and because the calling identity in your bucket’s access logs is the legitimate AWS service endpoint, not an external ARN, GuardDuty and IAM Access Analyzer’s cross-account findings stay silent — Access Analyzer flags foreign account ARNs in Principal blocks, not service principals paired with an unscoped SourceAccount. Downstream SIEM pipelines parsing injected log objects as trusted CloudTrail data compound the damage, and storage costs climb from unauthenticated write volume nobody billed for.

The Lead Mechanic Fix

Pin both aws:SourceAccount and aws:SourceArn on every service-principal statement:n{n “Effect”: “Allow”,n “Principal”: {“Service”: “cloudtrail.amazonaws.com”},n “Action”: “s3:PutObject”,n “Resource”: “arn:aws:s3:::bucket/prefix/*”,n “Condition”: {n “StringEquals”: {“aws:SourceAccount”: “111122223333”},n “ArnLike”: {“aws:SourceArn”: “arn:aws:cloudtrail:eu-west-2:111122223333:trail/trailname”}n }n}nAudit live policies with aws s3api get-bucket-policy –bucket , script a check for any Service principal statement lacking both condition keys, and enforce compliance with a custom AWS Config rule that flags or denies PutBucketPolicy calls where a service Principal omits aws:SourceAccount.

03

Apply the safer control

Before you change production

Confirm the affected scope, export the current configuration, and test the replacement control in a non-production environment first.

Control to implement

Require aws:SourceAccount and aws:SourceArn conditions on every service-principal statement and enforce this with a custom AWS Config rule.

Validate the vendor-specific syntax in official documentation before applying it.

04

Verify, roll back or escalate

Verify

Re-run the detection test and a controlled negative-path test. Confirm the unsafe behaviour is blocked while approved traffic still succeeds.

Rollback

Restore the exported configuration if the new control blocks required production traffic, then narrow the policy before redeployment.

Escalate

Escalate when the blast radius is uncertain, the control cannot be tested safely, or remediation requires an outage or security exception.

After remediation

Further reading stays below the corrective workflow and is selected by platform, category and shared technical keywords.

Discover more

Connected KBY resources