Billed for unauthorized requests? Google Cloud Storage vs. AWS S3

Marcos Manuel Ortega
Google Cloud - Community
5 min readMay 11, 2024

--

A recent story highlighted how unauthorized access to an empty AWS S3 bucket can result in surprising charges. This raised the question: can unauthorized access to Google Cloud Storage (GCS) also lead to unexpected bills?

Catchup: what happened?

A bit over a week ago, an interesting Medium story popped-up into many social feeds:

It even made into Hacker News frontpage.

To sum up: a user found that unauthorized 403 requests to upload files to his AWS S3 empty bucket by other users were billed to his account, as his bucket ID was used as a default value into a popular open-source solution and thousands of deployments tried by mistake to upload backup data into his bucket.

This was quite worrisome, as could be exploited by a malicious actor to bring anyone down just knowing any of our S3 bucket IDs: not by DoS but by Denial-of-Wallet attacks.

This bore the question — would this also happen on Google Cloud Storage?

Spoiler alert: no, 403 PUT requests are not billed in GCS.

Considerations

Base scenario

The original story stated that 403 non-authorized requests to AWS S3 were billed.

We wanted to also verify whether in Google Cloud Storage 403 non-authorized PUT requests to private GCS buckets are billed or not.

Number of requests

As per the GCS pricing docs, listing and uploading objects are billed as Class A operations and downloading/reading objects as Class B operations. Pricing is per 1000 operations, so we aimed for a adequate number of requests to make sure we would notice the charges.

Requester pays disabled

In GCS and S3 you can opt-in for “requester pays”, where the user making the request is paying for it instead of the bucket’s owner, which is intended for other scenarios e.g. sharing public data, rather than e.g. public website static date. Therefore, it was intentionally disabled, as is the usual configuration.

“Always free” tier and billing exports

In Google Cloud, there is a free tier for Cloud Storage for some amount of Class A and B operations, so it would mask the costs of our requests.

Therefore, detailed usage cost data was exported to BigQuery and analyzed using SQL, where we could see the charges for operations even if they would fall in this free tier or be subjected to credits, discounts and promotions.

This cost data is exported to BigQuery with some latency (usually several hours, depending on service and SKU), so results were checked after 24 hours.

Simulating unauthorized access: the experiment

2 Google Cloud projects were created:

  1. gcs-unauthorized-requests
  2. gcs-requesting-instance

Note: Usually you would want to keep your project IDs private for security.

Billing

Billing was configured for both projects with an active, good-standing billing account, with detailed usage cost data exported to BigQuery.

Details for gcs-unauthorized-requests project

  • Required roles: Cloud Storage Admin
  • Enabled APIs: Cloud Storage (storage.googleapis.com), BigQuery (bigquery.googleapis.com, to analyze exported billing data, could also be run in another project)
  • Cloud Storage resources: location — regional, europe-southwest1, storage class — standard
  • Private bucket: bucket-name-redacted, label bucket-private

Details for gcs-requesting-instance project

  • Required roles: Compute Engine Admin, Service Account Admin
  • Enabled APIs: Compute Engine (compute.googleapis.com)
  • Networking — VPC & subnet: default
  • Networking — FW rules: default VPC default rules → SSH ingress enabled, all egress enabled
  • VM instance: requesting-instance
  • Region & zone: europe-southwest1-a (Madrid)
  • Machine type: e2-standard-16 (max. egress bandwidth of 16 Gbps)

(Would have been less expensive and recommended to use a spot VM instead of a regular one, but forgot to check the option… oops)

  • VM instance OS: Debian 12 (bookworm) boot disk from the GCP public image family
  • VM instance has GCP Cloud SDK already installed, as by default in (most) GCE images
  • VM instance with an ephemeral external IP
  • User-managed service account assigned to the VM without any roles/permissions assigned, to force a 403 response

The test

Test consisted in SSHing to requesting-instance VM and running this Bash command executing a 403 PUT request to the GCS bucket 2100 times (more than the 1000 needed, just in case):

for i in {1..2100};
do
printf "\niter $i\n" && \
gsutil cp private-bucket-file.txt \
gs://gcs-unauthorized-request-private;
done

BigQuery SQL query for checking results (after 24 hours, given the expected delay for the billing export):

SELECT
project.id as project_id,
service.description as service_description,
sku.description as sku_description,
usage_start_time,
usage_end_time,
project.id,
labels,
location.region,
resource.global_name,
usage.amount,
usage.unit,
usage.amount_in_pricing_units,
usage.pricing_unit
FROM
`REDACTED.billing_export.gcp_billing_export_resource_v1_REDACTED`
WHERE
usage_start_time >= TIMESTAMP("2024-05-03")
AND usage_start_time < TIMESTAMP("2024-05-08")
AND (project.id = 'gcs-unauthorized-requests'
OR project.id = 'gcs-requesting-instance')
AND service.description = 'Cloud Storage'
ORDER BY
usage_start_time

Experiment results

Remember: we wanted to check if 403 PUT requests to a private GCS bucket are billed.

After executing the previous bash command:

  • Response status code: 403
  • Response: “AccessDeniedException: 403 REDACTED@gcs-requesting-instance.iam.gserviceaccount.com does not have storage.objects.create access to the Google Cloud Storage object.
    Permission ‘storage.objects.create’ denied on resource (or it may not exist).”
  • Number of requests: 2100
  • Start: 24/05/07 08:26 CEST
  • End: 24/05/07 09:19 CEST

After waiting +24 hours, we ran the query in the BigQuery billing dataset.

SQL query results: No “Regional Standard Class A Operations” were shown for these projects and dates

Verdict: No charges for unauthorized access

The good news: Google Cloud Storage doesn’t bill for unauthorized PUT requests, neither to the private bucket owner nor the unauthorized requester.

What This Means for You

Unlike AWS S3, Google Cloud Storage protects you from this “Denial-of-Wallet” attack where unauthorized access can inflate your bill. This adds another layer of security and cost control for your Cloud Storage buckets.

Further Exploration

While unauthorized PUT requests are free, we plan to investigate billing for other access scenarios, including unauthorized GET requests and public bucket access attempts. In particular,

  1. Check if 403 GET requests to a private GCS bucket existing file are billed
  2. Check if 403 GET requests to a private GCS bucket non-existing file are billed
  3. Check if 403 PUT requests to a public GCS bucket are billed
  4. Check if 403 GET requests to a public GCS bucket non-existing file are billed

Special thanks to

This article was jointly written by Marcos Manuel Ortega (LinkedIn), director at Indavelopers, and Julio Quinteros P. (@jquinterosp), Data & AI/ML manager/practice lead at Axmos

--

--