HIGH api key exposurerailsfirestore

Api Key Exposure in Rails with Firestore

Api Key Exposure in Rails with Firestore — how this specific combination creates or exposes the vulnerability

When a Ruby on Rails application interacts with Google Cloud Firestore, developers often store the Firestore service account key as a JSON file on disk or embed it in environment variables. If the Rails app is misconfigured or accidentally exposes these files through source control, logs, or debug endpoints, an API key exposure occurs. In this context, the Firestore key functions as a bearer token: any holder can make requests to the project’s Firestore REST or gRPC interfaces as permitted by the key’s IAM bindings.

Rails applications commonly load the key via GOOGLE_APPLICATION_CREDENTIALS or by reading a JSON file directly. If the Rails app runs in a container or shared host where the filesystem is inspectable, or if error messages include stack traces pointing to the key location, the key may be discoverable. For example, a verbose Rails log that prints the contents of ENV["FIRESTORE_KEY_JSON"] would constitute an API key exposure. Similarly, a misconfigured Rails route such as /debug/firestore_key that returns the key in plaintext would create a direct exposure path.

Once exposed, an attacker can use the key to read or write documents, depending on the service account’s roles (e.g., roles/datastore.user or more permissive roles). This can lead to data exfiltration, unauthorized modification of application state, or pivoting to other Google Cloud services if the same key is reused. The risk is amplified when the Firestore database contains sensitive user data or when the Rails app is part of a larger system where Firestore permissions are broad.

An attacker may also leverage the exposed key to perform SSRF from the server side, reaching internal metadata services, or to abuse Firestore’s export functionality to move large datasets off the environment. Because the key is a long-lived credential, continued access is possible until the key is rotated and the exposure is remediated.

Firestore-Specific Remediation in Rails — concrete code fixes

Remediation centers on preventing the key from appearing in logs, URLs, or error responses, and on minimizing the scope of the key if it must be used. Avoid embedding the full service account JSON in Rails environment variables; instead use workload identity federation or short-lived credentials where possible.

When you must use a key file, store it outside the repository and reference it securely. In production, prefer the default Compute Engine or GKE service accounts so Rails can use Application Default Credentials without a key file. For local development, use a restricted service account with the least privilege necessary.

To limit impact, scope the service account to only the Firestore operations required. For example, if the app only needs to read configuration documents, grant datastore.entities.get on specific document paths rather than datastore.entities.list or owner roles. Use Firestore’s native security rules to add an additional layer of access control for user-facing requests, ensuring that server-side keys are not relied upon for per-user authorization alone.

Example: Safe Firestore client initialization in Rails

Initialize the Firestore client without exposing the key in logs or error output. Avoid printing the client or its configuration in Rails console or views.

require "google/cloud/firestore"

# Use Application Default Credentials in production (no key file).
# On your development machine, set GOOGLE_APPLICATION_CREDENTIALS to a restricted key.
begin
  firestore = Google::Cloud::Firestore.new
rescue => e
  Rails.logger.error "Firestore initialization failed"
  # Do NOT log e.message if it may contain project or credential details
  raise "Unable to connect to Firestore"
end

Example: Controlled document access with explicit project and credentials

When you must specify a project ID explicitly, avoid leaking the key in URLs or logs. Use environment variables that are scrubbed from logs and do not include the full JSON in error messages.

project_id = ENV.fetch("GCLOUD_PROJECT")
# key_path is used by the client library via ADC; do not print key_path.
key_path = ENV.fetch("FIRESTORE_KEY_PATH")

begin
  firestore = Google::Cloud::Firestore.new project: project_id, keyfile: key_path
  docs = firestore.collections("users").where("active", "==", true).get
  docs.each do |doc|
    Rails.logger.debug "Loaded user document #{doc.id}"
    # Avoid logging document contents that may contain PII
  end
rescue Google::Cloud::Error => e
  Rails.logger.error "Firestore read error"
  raise "Unable to retrieve data"
end

Example: Minimal read-only access via environment configuration

Configure the Rails app to use a service account restricted to read-only access on specific collections. Never generate or expose the key via Rails routes or debugging endpoints.

# config/initializers/firestore.rb
require "google/cloud/firestore"

project_id = ENV.fetch("GCLOUD_PROJECT")
# keyfile is set only if a key is necessary; otherwise rely on ADC.
if ENV["FIRESTORE_KEY_PATH"].present?
  firestore = Google::Cloud::Firestore.new project: project_id, keyfile: ENV["FIRESTORE_KEY_PATH"]
else
  firestore = Google::Cloud::Firestore.new project: project_id
end

# Use the client safely within service objects; do not expose it globally.
module FirestoreService
  def self.users
    firestore.collections("users").where("suspended", "==", false).get
  end
end

Security hygiene notes

  • Do not include the service account JSON in version control or in Rails secrets.yml or credentials.yml.enc as plain text.
  • Rotate keys immediately if any exposure is suspected. In Google Cloud, disable the key and create a new one with restricted permissions.
  • Audit IAM bindings regularly: ensure the service account does not have broader roles than necessary.
  • Use Rails log filtering to prevent sensitive environment variables from appearing in logs.

Frequently Asked Questions

How can I detect if my Firestore key has been exposed in a Rails app?
Review Rails logs and source control for any presence of service account JSON content or full key strings. Monitor for unexpected outbound network calls from the server to Firestore endpoints that could indicate key usage. Use automated secret scanning in CI/CD to catch accidental commits.
Is it safe to rely on Firestore security rules alone without restricting the service account key?
Security rules provide user-level access control for client-side requests, but server-side keys bypass rules. Always restrict the service account key to the minimum required IAM permissions and use security rules as a complementary layer, not a replacement for key scoping.