S3 Activity Monitoring Audit
A private S3 bucket had no record of who read, wrote or deleted its objects. I turned on CloudTrail S3 data events and server access logging, and showed a GetObject could now be traced to the IAM identity behind it.
Status: ENABLED · VERIFIED
TOOLS
Objective
I set out to find monitoring blind spots on an S3 data store, check whether object-level access was being recorded, and turn on the logging needed to see it.
Scenario
A customer keeps sensitive data mostly in S3. The bucket was not public and ACLs were disabled, but object activity such as GetObject, PutObject and DeleteObject might be happening with no record. I wanted to know whether that activity could be detected and traced to whoever did it.
What I found
IncludeManagementEvents: true
DataResources: []CloudTrail was not capturing S3 data events
The trail recorded management events only. Its event selectors had no data resources.
A CloudTrail lookup showed bucket-level calls such as ListBuckets and GetBucketAcl, but nothing about individual objects. A CloudWatch Logs query for GetObject, PutObject and DeleteObject returned recordsMatched: 0.
Server access logging was off
The bucket had no server access logging configuration, so HTTP requests to its objects were not recorded there either.
What I did
- Reviewed the bucket
I identified it and listed its contents.
- Checked server access logging
It returned no configuration.
- Read the trail's event selectors
Management events only, no data resources.
- Queried for object activity
CloudTrail and CloudWatch Logs showed no object-level records.
- Turned on S3 data events
I added the bucket's objects to the trail's event selectors, keeping management events on.
- Turned on server access logging
I pointed it at a separate logging bucket with a prefix per source bucket.
EVIDENCE
aws cloudtrail put-event-selectors --trail-name "$TRAIL_NAME" --event-selectors "[{
\"ReadWriteType\": \"All\", \"IncludeManagementEvents\": true,
\"DataResources\": [{ \"Type\": \"AWS::S3::Object\", \"Values\": [\"arn:aws:s3:::${BUCKET_NAME}/\"] }]
}]"
aws s3api put-bucket-logging --bucket "$BUCKET_NAME" --bucket-logging-status "{
\"LoggingEnabled\": { \"TargetBucket\": \"$LOGGING_BUCKET\", \"TargetPrefix\": \"${BUCKET_NAME}/\" }
}"Validation
The trail now selects the bucket's objects, server access logging is on, and a test read showed up as a data event tied to an identity (account details masked):
Event selector: Type AWS::S3::Object, Values arn:aws:s3:::blind-bucket/
Data event: eventName GetObject, key flag.txt, userIdentity.arn arn:aws:iam::<ACCOUNT_ID>:user/<USER>
Access logging: TargetBucket blind-bucket-logs, TargetPrefix blind-bucket/Before and after
| CONTROL | BEFORE | AFTER |
|---|---|---|
| Server access logging | Not configured | Enabled |
| CloudTrail management events | Enabled | Enabled |
| CloudTrail S3 data events | Not configured | Enabled |
GetObject visibility | Not captured | Captured |
| Identity behind object activity | Not available | Recorded |
| Logs query for object events | 0 matches | Activity found |
What I recommend
- 01Turn on CloudTrail S3 data events for sensitive or business-critical buckets.
- 02Add server access logging where you need HTTP-level detail.
- 03Send logs to a dedicated, locked-down logging destination with set retention.
- 04Watch
GetObject,PutObjectandDeleteObjecton sensitive data, with saved queries or alerts. - 05Review logging coverage so new buckets do not reopen the gap.
“A private bucket can still be a blind spot if object-level activity is not logged and tied to an identity.”
SKILLS IT PROVES
- Cloud Security
- Amazon S3 Security
- AWS CloudTrail
- Audit Logging
- Security Monitoring
- Log Analysis
- Incident Response
- AWS CLI