Skip to main content

S3 Signed Upload Must Bind To Exact Object Key

ID: CTL.S3.WRITE.SCOPE.001 Category: Access Control Severity: High

What This Checks

Signed upload policies must restrict write permission to a single exact object key. Prefix-wide permissions (e.g., starts-with $key files/) are flagged as unsafe because they enable arbitrary overwrite and cross-tenant tampering.

Why It Matters

Presigned S3 upload URLs are commonly used to let end users upload files directly to S3 without routing through your application server. When the upload policy uses a starts-with condition instead of an eq condition on the object key, the user can write to any key under that prefix. On a bucket like acme-insurance-document-uploads, a prefix-scoped policy meant for claims/2026/doc-abc123.pdf actually allows writing to claims/2026/doc-anything.pdf -- or overwriting another customer's claim document. Binding each signed upload to an exact key generated server-side eliminates this class of overwrite vulnerability entirely.

What A Violation Looks Like

$ stave apply --controls controls/s3 --observations ./observations --max-unsafe 0s --now 2026-01-15T00:00:00Z
{
"control_id": "CTL.S3.WRITE.SCOPE.001",
"control_name": "S3 Signed Upload Must Bind To Exact Object Key",
"resource_id": "upload-api-acme-insurance-document-uploads",
"resource_type": "s3_upload_policy",
"resource_vendor": "aws",
"evidence": {
"matched_properties": [
{
"path": "s3_upload.allowed_key_mode",
"value": "prefix"
},
{
"path": "s3_upload.operation",
"value": "write"
},
{
"path": "type",
"value": "s3_upload_policy"
}
],
"first_unsafe_at": "2026-01-07T00:00:00Z",
"last_seen_unsafe_at": "2026-01-15T00:00:00Z",
"unsafe_duration_hours": 192,
"threshold_hours": 0,
"why_now": "Resource has been unsafe for 192 hours (threshold: 0 hours). Unsafe since 2026-01-07T00:00:00Z."
},
"mitigation": {
"description": "Control violation detected.",
"action": "Review the unsafe configuration and remediate."
}
}

Correct Configuration

A safe upload policy binds each signed upload to an exact object key:

{
"s3_upload": {
"operation": "write",
"allowed_key_mode": "exact"
}
}

To remediate: Change the signed upload policy to use an exact key condition (eq instead of starts-with) that binds each upload to a specific object path. Generate unique object keys server-side using a UUID or content-addressable hash so that clients cannot control the destination key.

  • CTL.S3.TENANT.ISOLATION.001 -- Shared-Bucket Tenant Isolation Must Enforce Prefix (flags missing prefix enforcement on shared buckets, which compounds prefix-scoped upload risks)
  • CTL.S3.ACCESS.003 -- No External Write Access (flags external write grants at the bucket policy level)
  • CTL.S3.ACCESS.002 -- No Wildcard Action Policies (flags s3:* grants that implicitly include write operations)