Back to Checks
CloudTrail trail records S3 object-level read events for all S3 buckets
cloudtrail_s3_dataevents_read_enabled
Service: cloudtrailSeverity: low
OfficialBuilt-inVerified
Categories:loggingforensics-ready
Resource Type:AwsCloudTrailTrail
CloudTrail trails log S3 object-level read data events for all buckets, capturing object access (for example GetObject) via selectors targeting AWS::S3::Object
Risk
Without object-level read logging, S3 access is opaque. Attackers or insiders can exfiltrate data via GetObject without audit trails, eroding confidentiality and hindering forensics, anomaly detection, and incident response.
Run this check with Prowler
Run in Prowler CloudRun
prowler aws --checks cloudtrail_s3_dataevents_read_enabledRecommendation
Enable CloudTrail data events for S3 objects with ReadOnly (or All) across all current and future buckets. Use a multi-Region trail, centralize logs in an encrypted bucket with lifecycle retention, and integrate monitoring/alerts to support defense in depth and accountable access.
Remediation
CLI
aws cloudtrail put-event-selectors --trail-name <example_resource_name> --event-selectors '[{"ReadWriteType":"ReadOnly","DataResources":[{"Type":"AWS::S3::Object","Values":["arn:aws:s3"]}]}]'Terraform
# Terraform: enable S3 object-level READ data events for all buckets on a trail
resource "aws_cloudtrail" "<example_resource_name>" {
name = "<example_resource_name>"
s3_bucket_name = "<example_resource_name>"
event_selector {
read_write_type = "ReadOnly" # CRITICAL: log read-only data events
data_resource {
type = "AWS::S3::Object" # CRITICAL: target S3 object-level events
values = ["arn:aws:s3"] # CRITICAL: apply to all S3 buckets/objects
}
}
}NativeIaC
# CloudFormation: enable S3 object-level READ data events for all buckets on a trail
Resources:
<example_resource_name>:
Type: AWS::CloudTrail::Trail
Properties:
S3BucketName: <example_resource_name>
EventSelectors:
- ReadWriteType: ReadOnly # CRITICAL: log read-only data events
DataResources:
- Type: AWS::S3::Object # CRITICAL: target S3 object-level events
Values:
- arn:aws:s3 # CRITICAL: applies to all S3 buckets/objectsOther
1In the AWS Console, open CloudTrail and select Trails
2Open your trail and go to the Data events section
3Add data event for S3 and choose All current and future S3 buckets
4Select only Read events (or All if Read-only is unavailable)
5Save changes
Check Code
from prowler.lib.check.models import Check, Check_Report_AWS
from prowler.providers.aws.services.cloudtrail.cloudtrail_client import (
cloudtrail_client,
)
from prowler.providers.aws.services.s3.s3_client import s3_client
class cloudtrail_s3_dataevents_read_enabled(Check):
def execute(self):
findings = []
if cloudtrail_client.trails is not None:
for trail in cloudtrail_client.trails.values():
for data_event in trail.data_events:
# classic event selectors
if not data_event.is_advanced:
# Check if trail has a data event for all S3 Buckets for read
if (
data_event.event_selector["ReadWriteType"] == "ReadOnly"
or data_event.event_selector["ReadWriteType"] == "All"
):
for resource in data_event.event_selector["DataResources"]:
if "AWS::S3::Object" == resource["Type"] and (
f"arn:{cloudtrail_client.audited_partition}:s3"
in resource["Values"]
or f"arn:{cloudtrail_client.audited_partition}:s3:::"
in resource["Values"]
or f"arn:{cloudtrail_client.audited_partition}:s3:::*/*"
in resource["Values"]
):
report = Check_Report_AWS(
metadata=self.metadata(),
resource=trail,
)
report.region = trail.home_region
report.status = "PASS"
report.status_extended = f"Trail {trail.name} from home region {trail.home_region} has a classic data event selector to record all S3 object-level API operations."
findings.append(report)
# advanced event selectors
elif data_event.is_advanced:
for field_selector in data_event.event_selector[
"FieldSelectors"
]:
if (
field_selector["Field"] == "resources.type"
and field_selector["Equals"][0] == "AWS::S3::Object"
):
report = Check_Report_AWS(
metadata=self.metadata(), resource=trail
)
report.region = trail.home_region
report.status = "PASS"
report.status_extended = f"Trail {trail.name} from home region {trail.home_region} has an advanced data event selector to record all S3 object-level API operations."
findings.append(report)
if not findings and (
s3_client.buckets or cloudtrail_client.provider.scan_unused_services
):
report = Check_Report_AWS(
metadata=self.metadata(), resource=cloudtrail_client.trails
)
report.region = cloudtrail_client.region
report.resource_arn = cloudtrail_client.trail_arn_template
report.resource_id = cloudtrail_client.audited_account
report.status = "FAIL"
report.status_extended = "No CloudTrail trails have a data event to record all S3 object-level API operations."
findings.append(report)
return findingsReferences
- https://awswala.medium.com/enable-cloudtrail-data-events-logging-for-objects-in-an-s3-bucket-33cade51ae2b
- https://docs.aws.amazon.com/securityhub/latest/userguide/s3-controls.html#s3-23
- https://docs.aws.amazon.com/AmazonS3/latest/userguide/enable-cloudtrail-logging-for-s3.html
- https://www.plerion.com/cloud-knowledge-base/ensure-object-level-logging-for-read-events-enabled-for-s3-bucket