Summary
All media uploaded to S3 is set ACL: public-read, making every received WhatsApp file world-readable to anyone who knows (or guesses) the URL.
Details
// s3manager.go:279
input := &s3.PutObjectInput{
...
ACL: types.ObjectCannedACLPublicRead,
}
Object keys follow a predictable pattern (users/{userID}/inbox/{contactJID}/.../{messageID}.ext). On S3-compatible backends without a bucket-level public-access block (common with MinIO / self-hosted), end-user media (images, documents, audio) is publicly accessible.
Proposed fix
Make access control configurable instead of always public:
- default to private objects and serve media via presigned GET URLs with a configurable TTL (
GetPublicURL would call PresignGetObject); or
- keep an explicit opt-in
s3_public_read flag for users who want public objects + a CDN.
This is a behavior change (some users rely on the static public URL), so it likely needs a flag + migration note — filing first to agree on the default. Happy to PR.
Location
s3manager.go:279 (and GetPublicURL)
Summary
All media uploaded to S3 is set
ACL: public-read, making every received WhatsApp file world-readable to anyone who knows (or guesses) the URL.Details
Object keys follow a predictable pattern (
users/{userID}/inbox/{contactJID}/.../{messageID}.ext). On S3-compatible backends without a bucket-level public-access block (common with MinIO / self-hosted), end-user media (images, documents, audio) is publicly accessible.Proposed fix
Make access control configurable instead of always public:
GetPublicURLwould callPresignGetObject); ors3_public_readflag for users who want public objects + a CDN.This is a behavior change (some users rely on the static public URL), so it likely needs a flag + migration note — filing first to agree on the default. Happy to PR.
Location
s3manager.go:279(andGetPublicURL)