Backblaze B2 stores get exposed through a mechanism distinct from AWS ACLs but
equally simple to accidentally trigger: a bucket-level type that controls whether every
file in the bucket is publicly downloadable. It is set at bucket creation but can be
changed at any time. Flip the type to allPublic and every file becomes downloadable by
any caller who knows or can guess its URL: no credentials, no signed request. Crucially,
this is anonymous download of known keys, not anonymous enumeration. There is no
unauthenticated way to list what a public B2 bucket contains, so exposure comes from
someone fetching individual file URLs they already know or can guess.
How B2 bucket types work
Every B2 bucket has one of two types. The type is set at creation in the Backblaze
console or via API, but it is not fixed there. It can be switched at any time with
b2_update_bucket:
allPrivate: the safe baseline. Files require an authorization token or a signed download URL to retrieve. No unauthenticated access.allPublic: every file in the bucket is publicly downloadable without any credentials. A caller who constructs the correct URL for a file can download it.
The bucket type applies uniformly across all files in the bucket. There is no per-object
visibility override in the way that S3 has object-level ACLs. If the bucket is allPublic,
every file is accessible.
Why teams set allPublic. Backblaze B2 has a long-standing partnership with
Cloudflare that makes egress from B2 to Cloudflare free. For teams hosting media, static
assets, or large file downloads, this is a significant cost advantage over AWS or GCS
egress rates. The typical deployment pattern: B2 bucket for storage, Cloudflare as the
CDN layer in front, with B2 set to allPublic so Cloudflare can fetch origin files
without managing authorization tokens. The problem is that allPublic exposes the
bucket directly, not just through Cloudflare, and many deployments leave the B2 origin
reachable alongside the CDN.
Why you can't just list a public B2 bucket
This is where B2 diverges sharply from S3, and where transplanted intuition often goes
wrong. On S3, an open bucket frequently leaks its full contents to an anonymous
?list-type=2 request. B2 does not work this way. Both of B2's listing surfaces
require authentication, regardless of bucket type:
- Native B2 API. The
b2_list_file_namescall returns a paginated list of files, but it requires an application key with thelistFilescapability. It is never an unauthenticated call. - S3-compatible endpoint. B2's S3-compatible API (exposed at
s3.<region>.backblazeb2.com) implements SigV4, and every list operation (including?list-type=2) must be SigV4-signed. This holds even forallPublicbuckets: there is no anonymousListBucketResultresponse to obtain. You cannot enumerate a public B2 bucket the way you can an open S3 bucket, and the S3-style enumeration probes that work against AWS simply return an auth error here.
What allPublic actually grants is anonymous download of individual objects whose
keys you already know. Given a filename, anyone can fetch it unauthenticated:
https://f<NNN>.backblazeb2.com/file/<bucket-name>/<file-key>
The f<NNN> download host is not something you can synthesize by guessing a shard
number. It is the account's downloadUrl, returned by b2_authorize_account, so the
correct host must be known or observed from a real link rather than fabricated. The same
object is also reachable over the S3-compatible path:
https://<bucket-name>.s3.<region>.backblazeb2.com/<file-key>
Exposure of an allPublic bucket is therefore a function of key knowledge, not free
enumeration. Filenames leak from front-end code, shared links, referrers, and
predictable naming schemes (backup-2026-06-01.sql, avatars/<user-id>.png). The
common real-world path is worse: allPublic buckets are often paired with broad
application keys embedded in client-side code for direct-upload flows. When such a
leaked key carries listFiles, an attacker enumerates the bucket authenticated and then
downloads every object anonymously. The listing capability, not the bucket type, is what
turns a public bucket into a fully harvestable one.
To check whether a specific B2 file URL is publicly downloadable, paste it into the open-bucket viewer.
What gets exposed
The content profile of a publicly accessible B2 bucket depends on why it was made public. For teams using B2 for the Cloudflare egress model, the exposed content is typically application assets: images, video, downloadable files, and build artifacts. That's a relatively contained exposure.
The more dangerous case is teams using B2 as a low-cost backup destination. B2's pricing
makes it attractive for bulk backup storage, and allPublic is sometimes set
inadvertently: chosen at bucket creation because the console default is visible and the
implications aren't obvious, or because a previous bucket was public and the workflow was
copy-pasted. A publicly accessible backup bucket can contain database exports,
application state, and credential-bearing configuration files.
Remediation
The fix is one setting. Everything else is hardening.
Set the bucket to allPrivate. In the Backblaze B2 console, open the bucket
settings and change the type from allPublic to allPrivate; via the CLI, the bucket
name and type are positional arguments:
b2 update-bucket <bucket-name> allPrivate
# v4 CLI: b2 bucket update <bucket-name> allPrivate
The change takes effect immediately: existing files stop being anonymously downloadable. Because the type is mutable, you can flip this control the moment you find an exposure; it is not baked in at creation.
Once the bucket is private, choose how legitimate consumers reach it. The two options pull in opposite directions:
- Serving files to end users? Use
b2_get_download_authorizationto mint a short-lived token scoped to a bucket prefix and expiry, generated server-side per request. Callers get temporary access to specific paths without the bucket ever being public. - Fronting B2 with the Cloudflare free-egress model? Keep the bucket
allPrivateand put a Cloudflare Worker in front as the origin proxy. The Worker holds a B2 application key as a Worker secret, authenticates on the CDN's behalf, and returns the file, preserving the free B2-to-Cloudflare egress while the origin stays unreachable directly.
Then tighten the keys. Any application key distributed in client-side code should carry
only writeFiles on the specific prefix it uploads to. Never listFiles, which is
what lets a leaked key enumerate the bucket.
If the bucket was public, act on the assumption it was read. Treat every object as potentially harvested: rotate any credentials, tokens, or secrets found in exposed files, and assess disclosure and privacy obligations for any personal data that was reachable. Be realistic about forensics. B2 has no native per-object access log in the console, so you generally cannot reconstruct who downloaded what. If a CDN like Cloudflare fronted the bucket, its request logs are your best (and often only) record of the exposure window.
For an index of exposed B2 buckets found by this service, see the Backblaze B2 exposure index. To probe a specific bucket or file URL right now, use the open-bucket viewer.