Storage & Data Security
Access Policies & Preventing Public Exposure
Public storage exposure remains the #1 cause of cloud data breaches. Even with improved defaults since 2023, intentional and accidental misconfigurations continue to expose sensitive data.
The Public Exposure Problem
Current statistics:
- 31% of S3 buckets are publicly accessible
- 46% of S3 buckets could be misconfigured
- Football Australia (2024): 127 storage containers exposed
The challenge isn't the default settings—it's developers creating exceptions for convenience that become permanent vulnerabilities.
AWS S3 Access Policy Patterns
Defense in Depth Approach
Account-level Block Public Access
↓
Bucket Policy (Deny public)
↓
IAM Policies (Least privilege)
↓
Access Points (Segmented access)
Account-Level Protection
Apply to the entire AWS account to prevent any S3 bucket from being public:
# Block public access at account level
aws s3control put-public-access-block \
--account-id 123456789012 \
--public-access-block-configuration \
BlockPublicAcls=true,\
IgnorePublicAcls=true,\
BlockPublicPolicy=true,\
RestrictPublicBuckets=true
Bucket Policy Templates
Deny external access (organization-only):
{
"Version": "2012-10-17",
"Statement": [{
"Sid": "DenyExternalAccess",
"Effect": "Deny",
"Principal": "*",
"Action": "s3:*",
"Resource": [
"arn:aws:s3:::my-bucket",
"arn:aws:s3:::my-bucket/*"
],
"Condition": {
"StringNotEquals": {
"aws:PrincipalOrgID": "o-xxxxxxxxxx"
}
}
}]
}
Restrict to specific VPC endpoint:
{
"Version": "2012-10-17",
"Statement": [{
"Sid": "DenyNonVPCAccess",
"Effect": "Deny",
"Principal": "*",
"Action": "s3:*",
"Resource": [
"arn:aws:s3:::my-bucket",
"arn:aws:s3:::my-bucket/*"
],
"Condition": {
"StringNotEquals": {
"aws:SourceVpce": "vpce-1234567890abcdef0"
}
}
}]
}
S3 Access Points
Modern approach for segmented access:
# Create access point for specific application
aws s3control create-access-point \
--account-id 123456789012 \
--name app-access-point \
--bucket my-bucket \
--vpc-configuration VpcId=vpc-12345678
# Access point policy
{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::123456789012:role/ApplicationRole"
},
"Action": ["s3:GetObject", "s3:PutObject"],
"Resource": "arn:aws:s3:us-east-1:123456789012:accesspoint/app-access-point/object/*"
}]
}
Azure Storage Access Controls
Disable Shared Key Access
Eliminate storage account keys entirely:
# Disable shared key access
az storage account update \
--name mystorageaccount \
--resource-group myRG \
--allow-shared-key-access false
Container Access Levels
| Level | Access | Recommendation |
|---|---|---|
| Private | No anonymous access | Default, always use |
| Blob | Anonymous read for blobs | Rarely needed |
| Container | Anonymous read for container + blobs | Avoid |
# Ensure container is private
az storage container set-permission \
--name mycontainer \
--account-name mystorageaccount \
--public-access off
SAS Token Best Practices
When temporary access is needed:
# Generate scoped SAS token
az storage container generate-sas \
--name mycontainer \
--account-name mystorageaccount \
--permissions r \
--expiry $(date -u -d "1 hour" +"%Y-%m-%dT%H:%MZ") \
--ip "10.0.0.0-10.0.0.255" \
--https-only
SAS token security rules:
- Short expiration (hours, not days)
- Minimum required permissions
- IP restrictions when possible
- HTTPS only
- Use stored access policies for revocation capability
GCP Cloud Storage Security
Public Access Prevention
Organization-wide enforcement:
# Enable public access prevention on bucket
gcloud storage buckets update gs://my-bucket \
--public-access-prevention=enforced
# Organization policy constraint
gcloud resource-manager org-policies set-policy policy.yaml
policy.yaml:
constraint: constraints/storage.publicAccessPrevention
listPolicy:
allValues: DENY
IAM Conditions
Time-limited or conditional access:
gcloud storage buckets add-iam-policy-binding gs://my-bucket \
--member="user:developer@example.com" \
--role="roles/storage.objectViewer" \
--condition="expression=request.time < timestamp('2026-02-01T00:00:00Z'),title=temporary-access"
Detecting Public Exposure
AWS Access Analyzer
# Enable IAM Access Analyzer
aws accessanalyzer create-analyzer \
--analyzer-name MyAnalyzer \
--type ACCOUNT
# List public/cross-account findings
aws accessanalyzer list-findings \
--analyzer-arn arn:aws:access-analyzer:us-east-1:123456789012:analyzer/MyAnalyzer \
--filter '{"resourceType": {"eq": ["AWS::S3::Bucket"]}}'
AWS Macie
Discover sensitive data in S3:
# Enable Macie
aws macie2 enable-macie
# Create sensitive data discovery job
aws macie2 create-classification-job \
--job-type ONE_TIME \
--s3-job-definition '{
"bucketDefinitions": [{
"accountId": "123456789012",
"buckets": ["my-bucket"]
}]
}'
Prevention Checklist
| Control | AWS | Azure | GCP |
|---|---|---|---|
| Block public access | ✓ Account + Bucket level | ✓ Private containers | ✓ Public access prevention |
| Organization policies | ✓ SCPs | ✓ Azure Policy | ✓ Org constraints |
| Access analyzer | ✓ IAM Access Analyzer | ✓ Defender for Cloud | ✓ Security Command Center |
| Data discovery | ✓ Macie | ✓ Purview | ✓ DLP |
| Monitoring | ✓ CloudTrail + Config | ✓ Diagnostic logs | ✓ Cloud Audit Logs |
Next, we'll cover data backup, disaster recovery, and data lifecycle management. :::