Why this matters
Object Storage is where a lot of Oracle Cloud teams keep backups, log exports, database dumps and files they share with partners. A bucket can be set to public, or a pre-authenticated request can be created for one object or every object in the bucket. Neither path needs a compute instance, a load balancer or an open network port. The data is served by Oracle Cloud Object Storage, so security lists and network security groups do not block it.
Public access comes in two bucket settings. ObjectRead lets anyone list the bucket and read objects. ObjectReadWithoutList blocks the listing but still lets anyone read an object when they know its name. Object names show up in web addresses, logs, support tickets and browser history, so the second setting is not a safe middle ground.
Pre-authenticated requests are bearer tokens. Anyone who has the request address can use it until it expires or is deleted, with no Oracle Cloud login. A request scoped to one object is a smaller problem than one scoped to any object in the bucket, but both need an owner and an expiry date. Finding these paths across every compartment and region is the job.
Before you start
You need read access to Object Storage and IAM in the tenancy. A read-only policy that can list buckets, read bucket metadata, list pre-authenticated requests and read policies is enough. Do not start with a role that can delete buckets or requests. You will be tempted to clean up as you go, and that can break a partner feed or a backup job before you know who owns it.
Collect the tenancy OCID, the Object Storage namespace and the list of regions the tenancy uses. Buckets are regional, and compartments can nest. A bucket in a child compartment in another region will not appear in a root compartment view in your home region. Oracle Cloud Shell works well for this because the CLI is already installed and authenticated.
Decide what evidence you will keep. For each bucket, record the name, OCID, compartment, region, public access type and any tags that name an owner. For each request, record the request ID, access type, object name or prefix and expiry. If you find a public write path, stop the inventory and treat it as an incident.
How to do it
Run the work from one shell and keep the output. Set your tenancy OCID and namespace first, then list compartments and regions. A simple loop over compartments and regions is more reliable than clicking through the Console, because the Console shows one region and one compartment at a time.
Classify each bucket by its public access type, then list its pre-authenticated requests. Do not trust the bucket list alone. Some CLI output and Console views hide the detail you need in the bucket get call or the request list. Write down the access type exactly as the API returns it so another person can check your work.
After the bucket pass, read IAM policies that mention Object Storage. A broad policy can grant access that the bucket setting does not show. Test one known object anonymously from a machine with no Oracle Cloud credentials. The response status tells you whether the path is real. The steps below are the order I would use.
- Set the tenancy and namespace. Run oci os ns get and export the returned namespace as NS. Export your tenancy OCID as TENANCY_OCID, then confirm both values with echo before you loop over anything.
- List every compartment. From the tenancy root, run oci iam compartment list --compartment-id $TENANCY_OCID --compartment-id-in-subtree true --all. Save the OCID and name of each compartment, and include the root itself.
- List the regions in use. Run oci iam region-subscription list and save the region names. If you have buckets in several regions, run the bucket loop once per region.
- List buckets per compartment and region. For each compartment and region, run oci os bucket list --compartment-id $C --namespace-name $NS --region $R --all. Save the bucket name and OCID, even when the bucket looks private.
- Read each bucket public access type. Run oci os bucket get --namespace-name $NS --bucket-name $B --region $R. Flag ObjectRead and ObjectReadWithoutList; NoPublicAccess is the private setting. Record the value, not just a yes or no.
- List pre-authenticated requests. For each bucket, run oci os preauth-request list --namespace-name $NS --bucket-name $B --region $R --all. Record id, access-type, time-expires and object-name. Flag unexpired requests, especially AnyObjectRead and AnyObjectReadWrite.
- Search IAM policies for broad grants. List policies in each compartment and read statements that name object-family, buckets or objects. Flag any-user statements and any manage object-family in tenancy grant that lacks a tight condition. A policy finding needs an owner and a reason.
- Test anonymous read on a known object. Take one object from a flagged bucket and request it from a machine with no OCI CLI config and no session. Use a 200 as proof of anonymous read; treat 401, 403 or 404 as no access through that path. Repeat for one object in each flagged bucket.
- Record findings and prepare fixes. Put the bucket OCID, region, compartment, public access type, request IDs and owner tag in one table. Set public buckets to private and delete unused requests only after the owner confirms the consumer. Keep a copy of the old setting for rollback.
How to check it worked
Re-run the bucket list and the bucket get call across every compartment and region. Count buckets whose public access type is not NoPublicAccess. The count should be zero unless you have an approved public site, and that exception should have an owner and a review date.
Re-list pre-authenticated requests for every bucket. Only approved requests should remain, with a short expiry and the narrowest object scope that works. Test the same object anonymously again. A 401, 403 or 404 is the expected result. A 200 means another path is still open.
Check that the application still works. A static site that used a public bucket will break when the bucket goes private, and a backup job that used a request will fail when the request is deleted. Look at Object Storage and Audit logs for the change window, then confirm the owner has a replacement path.
Mistakes to avoid
Checking only the root compartment and the home region is the most common miss. Teams create compartments for projects, and a bucket can live in a region nobody uses for compute. Run the inventory recursively and per region, then save the output with a date.
Do not treat ObjectReadWithoutList as private. It stops a directory listing, not a read. If an object name is in a ticket or a log line, the object is exposed. The same goes for a public write request: a token that can upload can also overwrite or plant files.
Do not delete a request the moment you find it. Pre-authenticated requests are often used by partners, CI jobs and backup scripts, and the owner may be on another team. Find the consumer in tags, policy names and Audit logs first, then replace the request with a short expiry and a narrow object scope.
A private bucket is not proof that no public path exists. A policy can grant broad access, and a request can sit in a bucket that looks clean in the Console. Test from outside. If you want an outside read-only pass, the Cloud security review from Cloudverg is $6,500 at a fixed price and covers public exposure checks across up to 5 cloud accounts, as one option among others.
Frequently asked questions
How do I find public buckets in Oracle Cloud?
Run oci os bucket list and then oci os bucket get for each bucket, or open each bucket in the Console. Look at the public access type. ObjectRead and ObjectReadWithoutList allow anonymous reads; NoPublicAccess does not.
Do Oracle Cloud pre-authenticated requests expire?
They expire only if an expiry was set when the request was created. Check the time-expires field and delete requests that have no owner or no current use. A request that points at any object in the bucket is the highest risk.
Can a private Oracle Cloud bucket still leak data?
Yes, through a pre-authenticated request or a broad IAM policy. The bucket setting controls anonymous bucket access, not every path to the objects. List requests and read policies before you call the bucket private.